From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Patrick Yavitz Date: Fri, 21 Jun 2024 11:54:06 -0400 Subject: add spacemit patch set source: https://gitee.com/bianbu-linux/linux-6.1 Signed-off-by: Patrick Yavitz --- drivers/spi/Kconfig | 20 + drivers/spi/Makefile | 3 + drivers/spi/spi-dw-espi.c | 1256 +++++++ drivers/spi/spi-dw-espi.h | 325 ++ drivers/spi/spi-dw-mmio-ext.c | 171 + drivers/spi/spi-k1x-dma.c | 375 ++ drivers/spi/spi-k1x-qspi.c | 1722 ++++++++++ drivers/spi/spi-k1x.c | 1189 +++++++ drivers/spi/spi-k1x.h | 364 ++ 9 files changed, 5425 insertions(+) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 111111111111..222222222222 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -272,6 +272,26 @@ config SPI_DAVINCI help SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_DESIGNWARE_EXT + tristate "DesignWare enhanced SPI controller core support" + imply SPI_MEM + help + general driver for enhanced SPI controller core from DesignWare + +config SPI_K1X + tristate "K1X SPI Controller" + depends on SOC_SPACEMIT_K1X + help + Enable support for the Spacemit K1X SPI controller. + +config SPI_K1X_QSPI + tristate "K1X QuadSPI Controller" + depends on SPI_MEM + help + This enables support for the Spacemit K1X QuadSPI controller in master mode. + This controller does only support the high-level SPI memory interface + and not support generic SPI messages. + config SPI_DESIGNWARE tristate "DesignWare SPI controller core support" imply SPI_MEM diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 111111111111..222222222222 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,9 @@ obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DLN2) += spi-dln2.o +obj-$(CONFIG_SPI_DESIGNWARE_EXT) += spi-dw-mmio-ext.o spi-dw-espi.o +obj-$(CONFIG_SPI_K1X) += spi-k1x.o spi-k1x-dma.o +obj-$(CONFIG_SPI_K1X_QSPI) += spi-k1x-qspi.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o spi-dw-y := spi-dw-core.o spi-dw-$(CONFIG_SPI_DW_DMA) += spi-dw-dma.o diff --git a/drivers/spi/spi-dw-espi.c b/drivers/spi/spi-dw-espi.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-dw-espi.c @@ -0,0 +1,1256 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Designware SPI core controller driver + * + * Copyright (c) 2023, spacemit Corporation. + * + * base on design-ware spi-core driver(spi-dw-xxx.c) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-dw-espi.h" + +#ifdef CONFIG_DEBUG_FS +#include +#endif + +/* Slave spi_device related */ +struct dw_spi_chip_data { + u32 cr0; + u32 rx_sample_dly; /* RX sample delay */ +}; + +#ifdef CONFIG_DEBUG_FS + +#define DW_SPI_DBGFS_REG(_name, _off) \ +{ \ + .name = _name, \ + .offset = _off, \ +} + +static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = { + DW_SPI_DBGFS_REG("CTRLR0", DW_SPI_CTRLR0), + DW_SPI_DBGFS_REG("CTRLR1", DW_SPI_CTRLR1), + DW_SPI_DBGFS_REG("SSIENR", DW_SPI_SSIENR), + DW_SPI_DBGFS_REG("SER", DW_SPI_SER), + DW_SPI_DBGFS_REG("BAUDR", DW_SPI_BAUDR), + DW_SPI_DBGFS_REG("TXFTLR", DW_SPI_TXFTLR), + DW_SPI_DBGFS_REG("RXFTLR", DW_SPI_RXFTLR), + DW_SPI_DBGFS_REG("TXFLR", DW_SPI_TXFLR), + DW_SPI_DBGFS_REG("RXFLR", DW_SPI_RXFLR), + DW_SPI_DBGFS_REG("SR", DW_SPI_SR), + DW_SPI_DBGFS_REG("IMR", DW_SPI_IMR), + DW_SPI_DBGFS_REG("ISR", DW_SPI_ISR), + DW_SPI_DBGFS_REG("DMACR", DW_SPI_DMACR), + DW_SPI_DBGFS_REG("DMATDLR", DW_SPI_DMATDLR), + DW_SPI_DBGFS_REG("DMARDLR", DW_SPI_DMARDLR), + DW_SPI_DBGFS_REG("RX_SAMPLE_DLY", DW_SPI_RX_SAMPLE_DLY), +}; + +static int dw_spi_debugfs_init(struct dw_spi *dws) +{ + char name[32]; + + snprintf(name, 32, "dw_spi%d", dws->master->bus_num); + dws->debugfs = debugfs_create_dir(name, NULL); + if (!dws->debugfs) + return -ENOMEM; + + dws->regset.regs = dw_spi_dbgfs_regs; + dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs); + dws->regset.base = dws->regs; + debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset); + + return 0; +} + +static void dw_spi_debugfs_remove(struct dw_spi *dws) +{ + debugfs_remove_recursive(dws->debugfs); +} + +#else +static inline int dw_spi_debugfs_init(struct dw_spi *dws) +{ + return 0; +} + +static inline void dw_spi_debugfs_remove(struct dw_spi *dws) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +void dw_spi_ext_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_controller_get_devdata(spi->controller); + bool cs_high = !!(spi->mode & SPI_CS_HIGH); + + /* + * DW SPI controller demands any native CS being set in order to + * proceed with data transfer. So in order to activate the SPI + * communications we must set a corresponding bit in the Slave + * Enable register no matter whether the SPI core is configured to + * support active-high or active-low CS level. + */ + if (cs_high == enable) + dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); + else + dw_writel(dws, DW_SPI_SER, 0); +} + +/* Return the max entries we can fill into tx fifo */ +static inline u32 dw_spi_tx_max(struct dw_spi *dws) +{ + u32 tx_room, rxtx_gap; + + tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); + + /* + * Another concern is about the tx/rx mismatch, we + * though to use (dws->fifo_len - rxflr - txflr) as + * one maximum value for tx, but it doesn't cover the + * data which is out of tx/rx fifo and inside the + * shift registers. So a control from sw point of + * view is taken. + */ + rxtx_gap = dws->fifo_len - (dws->rx_len - dws->tx_len); + + return min3((u32)dws->tx_len, tx_room, rxtx_gap); +} + +/* Return the max entries we should read out of rx fifo */ +static inline u32 dw_spi_rx_max(struct dw_spi *dws) +{ + return min_t(u32, dws->rx_len, dw_readl(dws, DW_SPI_RXFLR)); +} + +static void dw_writer(struct dw_spi *dws) +{ + u32 max = dw_spi_tx_max(dws); + u32 txw = 0; + + while (max--) { + if (dws->tx) { + if (dws->n_bytes == 1) + txw = *(u8 *)(dws->tx); + else if (dws->n_bytes == 2) + txw = *(u16 *)(dws->tx); + else + txw = *(u32 *)(dws->tx); + + dws->tx += dws->n_bytes; + } + dw_write_io_reg(dws, DW_SPI_DR, txw); + --dws->tx_len; + } +} + +static void dw_reader(struct dw_spi *dws) +{ + u32 max = dw_spi_rx_max(dws); + u32 rxw; + + while (max--) { + rxw = dw_read_io_reg(dws, DW_SPI_DR); + if (dws->rx) { + if (dws->n_bytes == 1) + *(u8 *)(dws->rx) = rxw; + else if (dws->n_bytes == 2) + *(u16 *)(dws->rx) = rxw; + else + *(u32 *)(dws->rx) = rxw; + + dws->rx += dws->n_bytes; + } + --dws->rx_len; + } +} + +int dw_spi_ext_check_status(struct dw_spi *dws, bool raw) +{ + u32 irq_status; + int ret = 0; + + if (raw) + irq_status = dw_readl(dws, DW_SPI_RISR); + else + irq_status = dw_readl(dws, DW_SPI_ISR); + + if (irq_status & DW_SPI_INT_RXOI) { + dev_err(&dws->master->dev, "RX FIFO overflow detected\n"); + ret = -EIO; + } + + if (irq_status & DW_SPI_INT_RXUI) { + dev_err(&dws->master->dev, "RX FIFO underflow detected\n"); + ret = -EIO; + } + + if (irq_status & DW_SPI_INT_TXOI) { + dev_err(&dws->master->dev, "TX FIFO overflow detected\n"); + ret = -EIO; + } + + /* Generically handle the erroneous situation */ + if (ret) { + dw_spi_reset_chip(dws); + if (dws->master->cur_msg) + dws->master->cur_msg->status = ret; + } + + return ret; +} + +static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) +{ + u16 irq_status = dw_readl(dws, DW_SPI_ISR); + + if (dw_spi_ext_check_status(dws, false)) { + spi_finalize_current_transfer(dws->master); + return IRQ_HANDLED; + } + + /* + * Read data from the Rx FIFO every time we've got a chance executing + * this method. If there is nothing left to receive, terminate the + * procedure. Otherwise adjust the Rx FIFO Threshold level if it's a + * final stage of the transfer. By doing so we'll get the next IRQ + * right when the leftover incoming data is received. + */ + dw_reader(dws); + if (!dws->rx_len) { + dw_spi_mask_intr(dws, 0xff); + spi_finalize_current_transfer(dws->master); + } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { + dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); + } + + /* + * Send data out if Tx FIFO Empty IRQ is received. The IRQ will be + * disabled after the data transmission is finished so not to + * have the TXE IRQ flood at the final stage of the transfer. + */ + if (irq_status & DW_SPI_INT_TXEI) { + dw_writer(dws); + if (!dws->tx_len) + dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dw_spi_ext_irq(int irq, void *dev_id) +{ + struct spi_controller *master = dev_id; + struct dw_spi *dws = spi_controller_get_devdata(master); + u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK; + + if (!irq_status) + return IRQ_NONE; + + if (!master->cur_msg) { + dw_spi_mask_intr(dws, 0xff); + return IRQ_HANDLED; + } + + return dws->transfer_handler(dws); +} + +static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi) +{ + u32 cr0 = 0; + + if (dw_spi_ip_is(dws, PSSI)) { + /* CTRLR0[ 5: 4] Frame Format */ + cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_FRF_MASK, DW_SPI_CTRLR0_FRF_MOTO_SPI); + + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 6] Serial Clock Phase + * CTRLR0[ 7] Serial Clock Polarity + */ + if (spi->mode & SPI_CPOL) + cr0 |= DW_PSSI_CTRLR0_SCPOL; + if (spi->mode & SPI_CPHA) + cr0 |= DW_PSSI_CTRLR0_SCPHA; + + /* CTRLR0[11] Shift Register Loop */ + if (spi->mode & SPI_LOOP) + cr0 |= DW_PSSI_CTRLR0_SRL; + } else { + /* CTRLR0[ 7: 6] Frame Format */ + cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_FRF_MASK, DW_SPI_CTRLR0_FRF_MOTO_SPI); + + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 8] Serial Clock Phase + * CTRLR0[ 9] Serial Clock Polarity + */ + if (spi->mode & SPI_CPOL) + cr0 |= DW_HSSI_CTRLR0_SCPOL; + if (spi->mode & SPI_CPHA) + cr0 |= DW_HSSI_CTRLR0_SCPHA; + + /* CTRLR0[13] Shift Register Loop */ + if (spi->mode & SPI_LOOP) + cr0 |= DW_HSSI_CTRLR0_SRL; + + /* CTRLR0[31] MST */ + if (dw_spi_ver_is_ge(dws, HSSI, 102A)) + cr0 |= DW_HSSI_CTRLR0_MST; + } + + return cr0; +} + +void dw_spi_ext_update_config(struct dw_spi *dws, struct spi_device *spi, + struct dw_spi_cfg *cfg) +{ + struct dw_spi_chip_data *chip = spi_get_ctldata(spi); + u32 cr0 = chip->cr0; + u32 speed_hz; + u16 clk_div; + + /* CTRLR0[ 4/3: 0] or CTRLR0[ 20: 16] Data Frame Size */ + cr0 |= (cfg->dfs - 1) << dws->dfs_offset; + + if (dw_spi_ip_is(dws, PSSI)) + /* CTRLR0[ 9:8] Transfer Mode */ + cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_TMOD_MASK, cfg->tmode); + else + /* CTRLR0[11:10] Transfer Mode */ + cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_TMOD_MASK, cfg->tmode); + + if (dws->caps & DW_SPI_CAP_EXT_SPI) { + if (cfg->spi_frf) + cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_SPI_FRF_MASK, + cfg->spi_frf); + else + cr0 &= ~DW_HSSI_CTRLR0_SPI_FRF_MASK; + } + + dw_writel(dws, DW_SPI_CTRLR0, cr0); + + if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD || + cfg->tmode == DW_SPI_CTRLR0_TMOD_RO || + (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO && + (dws->caps & DW_SPI_CAP_EXT_SPI) && cfg->spi_frf)) + dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); + + /* Note DW APB SSI clock divider doesn't support odd numbers */ + clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe; + speed_hz = dws->max_freq / clk_div; + + if (dws->current_freq != speed_hz) { + dw_spi_set_clk(dws, clk_div); + dws->current_freq = speed_hz; + } + + /* Update RX sample delay if required */ + if (dws->cur_rx_sample_dly != chip->rx_sample_dly) { + dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly); + dws->cur_rx_sample_dly = chip->rx_sample_dly; + } +} + +static void dw_spi_ext_irq_setup(struct dw_spi *dws) +{ + u16 level; + u8 imask; + + /* + * Originally Tx and Rx data lengths match. Rx FIFO Threshold level + * will be adjusted at the final stage of the IRQ-based SPI transfer + * execution so not to lose the leftover of the incoming data. + */ + level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len); + dw_writel(dws, DW_SPI_TXFTLR, level); + dw_writel(dws, DW_SPI_RXFTLR, level - 1); + + dws->transfer_handler = dw_spi_transfer_handler; + + imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI | + DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; + dw_spi_umask_intr(dws, imask); +} + +/* + * The iterative procedure of the poll-based transfer is simple: write as much + * as possible to the Tx FIFO, wait until the pending to receive data is ready + * to be read, read it from the Rx FIFO and check whether the performed + * procedure has been successful. + * + * Note this method the same way as the IRQ-based transfer won't work well for + * the SPI devices connected to the controller with native CS due to the + * automatic CS assertion/de-assertion. + */ +static int dw_spi_poll_transfer(struct dw_spi *dws, + struct spi_transfer *transfer) +{ + struct spi_delay delay; + u16 nbits; + int ret; + + delay.unit = SPI_DELAY_UNIT_SCK; + nbits = dws->n_bytes * BITS_PER_BYTE; + + do { + dw_writer(dws); + + delay.value = nbits * (dws->rx_len - dws->tx_len); + spi_delay_exec(&delay, transfer); + + dw_reader(dws); + + ret = dw_spi_ext_check_status(dws, true); + if (ret) + return ret; + } while (dws->rx_len); + + return 0; +} + +static int dw_spi_transfer_one(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + struct dw_spi_cfg cfg = { + .tmode = DW_SPI_CTRLR0_TMOD_TR, + .dfs = transfer->bits_per_word, + .freq = transfer->speed_hz, + .spi_frf = 0, + }; + int ret; + + dws->dma_mapped = 0; + dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); + dws->tx = (void *)transfer->tx_buf; + dws->tx_len = transfer->len / dws->n_bytes; + dws->rx = transfer->rx_buf; + dws->rx_len = dws->tx_len; + + /* Ensure the data above is visible for all CPUs */ + smp_mb(); + + dw_spi_enable_chip(dws, 0); + + dw_spi_ext_update_config(dws, spi, &cfg); + + transfer->effective_speed_hz = dws->current_freq; + + /* Check if current transfer is a DMA transaction */ + if (master->can_dma && master->can_dma(master, spi, transfer)) + dws->dma_mapped = master->cur_msg_mapped; + + /* For poll mode just disable all interrupts */ + dw_spi_mask_intr(dws, 0xff); + + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_setup(dws, transfer); + if (ret) + return ret; + } + + dw_spi_enable_chip(dws, 1); + + if (dws->dma_mapped) + return dws->dma_ops->dma_transfer(dws, transfer); + else if (dws->irq == IRQ_NOTCONNECTED) + return dw_spi_poll_transfer(dws, transfer); + + dw_spi_ext_irq_setup(dws); + + return 1; +} + +static void dw_spi_handle_err(struct spi_controller *master, + struct spi_message *msg) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + + if (dws->dma_mapped) + dws->dma_ops->dma_stop(dws); + + dw_spi_reset_chip(dws); +} + +static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + if (op->data.dir == SPI_MEM_DATA_IN) + op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK + 1); + + return 0; +} + +static bool dw_spi_supports_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller); + + /* + * Only support the numbler of io lines used to transfer the cmd + * is 1 in enhanced SPI for now. + */ + if (op->addr.buswidth > 1 || op->dummy.buswidth > 1 || + op->cmd.buswidth > 1) + return false; + + /* In enhanced SPI 1, 2, 4, 8 all are valid modes. */ + if (op->data.buswidth > 1 && (!(dws->caps & DW_SPI_CAP_EXT_SPI))) + return false; + + /* Only support upto 32 bit address in enhanced SPI for now. */ + if (op->data.buswidth > 1 && op->addr.nbytes > 4) + return false; + + return spi_mem_default_supports_op(mem, op); +} + +static int dw_spi_init_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op, + bool enhanced_spi) +{ + unsigned int i, j, len; + u8 *out; + + /* + * Calculate the total length of the EEPROM command transfer and + * either use the pre-allocated buffer or create a temporary one. + */ + len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; + if (op->data.dir == SPI_MEM_DATA_OUT) + len += op->data.nbytes; + + if (len <= DW_SPI_BUF_SIZE) { + out = dws->buf; + } else { + out = kzalloc(len, GFP_KERNEL); + if (!out) + return -ENOMEM; + } + + /* + * Collect the operation code, address and dummy bytes into the single + * buffer. If it's a transfer with data to be sent, also copy it into the + * single buffer in order to speed the data transmission up. + */ + for (i = 0; i < op->cmd.nbytes; ++i) + out[i] = DW_SPI_GET_BYTE(op->cmd.opcode, op->cmd.nbytes - i - 1); + + if (enhanced_spi) { + /* + * Fill the remaining spaces of dws->reg_io_width bytes + * size register with zero for cmd. + */ + for (; i < dws->reg_io_width; ++i) + out[i] = 0; + /* + * Copy the address bytes in dws->reg_io_width bytes size + * register and fill remaining spaces with zero. + */ + for (j = op->addr.nbytes; j > 0; ++i, --j) + out[i] = DW_SPI_GET_BYTE(op->addr.val, op->addr.nbytes - j); + for (j = op->addr.nbytes; j < dws->reg_io_width; ++i, ++j) + out[i] = 0; + } else { + for (j = 0; j < op->addr.nbytes; ++i, ++j) + out[i] = DW_SPI_GET_BYTE(op->addr.val, op->addr.nbytes - j - 1); + } + + if (!enhanced_spi) { + /* + * dummy bytes are not needed in enhanced mode as + * wait_cycles specified as number of SPI clock cycles + * between control frames transmit and data reception + * will be mentioned in enhanced spi mode. + */ + for (j = 0; j < op->dummy.nbytes; ++i, ++j) + out[i] = 0x0; + } + + if (op->data.dir == SPI_MEM_DATA_OUT) + memcpy(&out[i], op->data.buf.out, op->data.nbytes); + + dws->n_bytes = 1; + dws->tx = out; + + if (enhanced_spi) { + /* + * In enhanced mode cmd will be one FIFO and address + * will be one more FIFO. + */ + dws->tx_len = 1; + if (op->addr.nbytes) + dws->tx_len += 1; + if (op->data.dir == SPI_MEM_DATA_OUT) + dws->tx_len += op->data.nbytes; + } else { + dws->tx_len = len; + } + + if (op->data.dir == SPI_MEM_DATA_IN) { + dws->rx = op->data.buf.in; + dws->rx_len = op->data.nbytes; + } else { + dws->rx = NULL; + dws->rx_len = 0; + } + + return 0; +} + +static void dw_spi_free_mem_buf(struct dw_spi *dws) +{ + if (dws->tx != dws->buf) + kfree(dws->tx); +} + +static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) +{ + u32 room, entries, sts; + unsigned int len; + u8 *buf; + + /* + * At initial stage we just pre-fill the Tx FIFO in with no rush, + * since native CS hasn't been enabled yet and the automatic data + * transmission won't start til we do that. + */ + len = min(dws->fifo_len, dws->tx_len); + buf = dws->tx; + while (len--) + dw_write_io_reg(dws, DW_SPI_DR, *buf++); + + /* + * After setting any bit in the SER register the transmission will + * start automatically. We have to keep up with that procedure + * otherwise the CS de-assertion will happen whereupon the memory + * operation will be pre-terminated. + */ + len = dws->tx_len - ((void *)buf - dws->tx); + dw_spi_ext_set_cs(spi, false); + while (len) { + entries = readl_relaxed(dws->regs + DW_SPI_TXFLR); + if (!entries) { + dev_err(&dws->master->dev, "CS de-assertion on Tx\n"); + return -EIO; + } + room = min(dws->fifo_len - entries, len); + for (; room; --room, --len) + dw_write_io_reg(dws, DW_SPI_DR, *buf++); + } + + /* + * Data fetching will start automatically if the EEPROM-read mode is + * activated. We have to keep up with the incoming data pace to + * prevent the Rx FIFO overflow causing the inbound data loss. + */ + len = dws->rx_len; + buf = dws->rx; + while (len) { + entries = readl_relaxed(dws->regs + DW_SPI_RXFLR); + if (!entries) { + sts = readl_relaxed(dws->regs + DW_SPI_RISR); + if (sts & DW_SPI_INT_RXOI) { + dev_err(&dws->master->dev, "FIFO overflow on Rx\n"); + return -EIO; + } + continue; + } + entries = min(entries, len); + for (; entries; --entries, --len) + *buf++ = dw_read_io_reg(dws, DW_SPI_DR); + } + + return 0; +} + +static inline bool dw_spi_ctlr_busy(struct dw_spi *dws) +{ + return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY; +} + +static int dw_spi_wait_mem_op_done(struct dw_spi *dws) +{ + int retry = DW_SPI_WAIT_RETRIES; + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + nents = dw_readl(dws, DW_SPI_TXFLR); + ns = NSEC_PER_SEC / dws->current_freq * nents; + ns *= dws->n_bytes * BITS_PER_BYTE; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + + while (dw_spi_ctlr_busy(dws) && retry--) + spi_delay_exec(&delay, NULL); + + if (retry < 0) { + dev_err(&dws->master->dev, "Mem op hanged up\n"); + return -EIO; + } + + return 0; +} + +static void ext_transfer_delay(struct dw_spi *dws) +{ + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + nents = dw_readl(dws, DW_SPI_TXFLR); + ns = NSEC_PER_SEC / dws->current_freq * nents; + ns *= dws->n_bytes * BITS_PER_BYTE; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + /* wait until there is some space in TX FIFO */ + while (!(dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_TF_NOT_FULL)) + spi_delay_exec(&delay, NULL); +} + +static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi) +{ + dw_spi_enable_chip(dws, 0); + dw_spi_ext_set_cs(spi, true); + dw_spi_enable_chip(dws, 1); +} + +static int enhanced_transfer(struct dw_spi *dws, struct spi_device *spi, + const struct spi_mem_op *op) +{ + u32 max, txw = 0, rxw; + bool cs_done = false; + void *buf = dws->tx; + int ret; + + /* Send cmd as 32 bit value */ + if (buf) { + txw = *(u32 *)(buf); + dw_write_io_reg(dws, DW_SPI_DR, txw); + buf += 4; + dws->tx_len--; + if (op->addr.nbytes) { + /* + * Send address as 32 bit value if address + * is present in the instruction. + */ + txw = *(u32 *)(buf); + dw_write_io_reg(dws, DW_SPI_DR, txw); + buf += 4; + dws->tx_len--; + } + } + + do { + max = min_t(u32, dws->tx_len, dws->fifo_len - + dw_readl(dws, DW_SPI_TXFLR)); + while (max--) { + if (buf) { + txw = *(u8 *)(buf); + buf += dws->n_bytes; + } + dw_write_io_reg(dws, DW_SPI_DR, txw); + --dws->tx_len; + } + /* Enable CS after filling up FIFO */ + if (!cs_done) { + dw_spi_ext_set_cs(spi, false); + cs_done = true; + } + ext_transfer_delay(dws); + if (!dws->tx_len && !dws->rx_len) { + /* + * We only need to wait for done if there is + * nothing to receive and there is nothing more + * to transmit. If we are receiving, then the + * wait cycles will make sure we wait. + */ + ret = dw_spi_wait_mem_op_done(dws); + if (ret) + return ret; + } + } while (dws->tx_len); + + buf = dws->rx; + while (dws->rx_len) { + max = dw_spi_rx_max(dws); + + while (max--) { + rxw = dw_read_io_reg(dws, DW_SPI_DR); + if (buf) { + *(u8 *)(buf) = rxw; + buf += dws->n_bytes; + } + --dws->rx_len; + } + + ret = dw_spi_ext_check_status(dws, true); + if (ret) + return ret; + } + return 0; +} + +static void update_spi_ctrl0(struct dw_spi *dws, const struct spi_mem_op *op, bool enable) +{ + u32 spi_ctrlr0; + u32 addr_l = 0; + + spi_ctrlr0 = dw_readl(dws, DW_SPI_SPI_CTRLR0); + if (enable) { + spi_ctrlr0 |= FIELD_PREP(DW_HSSI_SPI_CTRLR0_WAIT_CYCLE_MASK, + op->dummy.nbytes * BITS_PER_BYTE); + /* 8 bit instruction length */ + spi_ctrlr0 |= FIELD_PREP(DW_HSSI_SPI_CTRLR0_INST_L_MASK, + DW_HSSI_SPI_CTRLR0_INST_L8); + /* 32 bit address length */ + addr_l = clamp(op->addr.nbytes * 2, 0, 0xf); + spi_ctrlr0 |= FIELD_PREP(DW_HSSI_SPI_CTRLR0_ADDR_L_MASK, + addr_l); + /* Enable clock stretching */ + spi_ctrlr0 |= DW_HSSI_SPI_CTRLR0_CLK_STRETCH_EN; + } else { + spi_ctrlr0 &= ~DW_HSSI_SPI_CTRLR0_WAIT_CYCLE_MASK; + spi_ctrlr0 &= ~DW_HSSI_SPI_CTRLR0_INST_L_MASK; + spi_ctrlr0 &= ~DW_HSSI_SPI_CTRLR0_ADDR_L_MASK; + spi_ctrlr0 &= ~DW_HSSI_SPI_CTRLR0_CLK_STRETCH_EN; + } + + dw_writel(dws, DW_SPI_SPI_CTRLR0, spi_ctrlr0); +} + +/* + * The SPI memory operation implementation below is the best choice for the + * devices, which are selected by the native chip-select lane. It's + * specifically developed to workaround the problem with automatic chip-select + * lane toggle when there is no data in the Tx FIFO buffer. Luckily the current + * SPI-mem core calls exec_op() callback only if the GPIO-based CS is + * unavailable. + */ +static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller); + bool enhanced_spi = false; + struct dw_spi_cfg cfg; + unsigned long flags; + int ret; + + if (dws->caps & DW_SPI_CAP_EXT_SPI) { + switch (op->data.buswidth) { + case 2: + cfg.spi_frf = DW_SSI_CTRLR0_SPI_FRF_DUAL_SPI; + enhanced_spi = true; + break; + case 4: + cfg.spi_frf = DW_SSI_CTRLR0_SPI_FRF_QUAD_SPI; + enhanced_spi = true; + break; + case 8: + cfg.spi_frf = DW_SSI_CTRLR0_SPI_FRF_OCT_SPI; + enhanced_spi = true; + break; + default: + cfg.spi_frf = 0; + break; + } + } + + /* + * Collect the outbound data into a single buffer to speed the + * transmission up at least on the initial stage. + */ + ret = dw_spi_init_mem_buf(dws, op, enhanced_spi); + if (ret) + return ret; + + /* + * DW SPI EEPROM-read mode is required only for the SPI memory Data-IN + * operation. Transmit-only mode is suitable for the rest of them. + */ + cfg.dfs = 8; + cfg.freq = clamp(mem->spi->max_speed_hz, 0U, dws->max_mem_freq); + if (op->data.dir == SPI_MEM_DATA_IN) { + if (enhanced_spi) + cfg.tmode = DW_SPI_CTRLR0_TMOD_RO; + else + cfg.tmode = DW_SPI_CTRLR0_TMOD_EPROMREAD; + cfg.ndf = op->data.nbytes; + } else { + cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; + if (enhanced_spi) + cfg.ndf = op->data.nbytes; + } + + dw_spi_enable_chip(dws, 0); + + if (enhanced_spi) { + u16 level; + + level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len); + dw_writel(dws, DW_SPI_TXFTLR, level); + /* + * In enhanced mode if we are reading then tx_len is 0 as we + * have nothing to transmit. Calculate DW_SPI_RXFTLR with + * rx_len. + */ + if (dws->rx_len != 0) { + level = min_t(u16, dws->fifo_len / 2, dws->rx_len); + } + dw_writel(dws, DW_SPI_RXFTLR, level - 1); + } + + if (dws->caps & DW_SPI_CAP_EXT_SPI) + update_spi_ctrl0(dws, op, enhanced_spi); + + dw_spi_ext_update_config(dws, mem->spi, &cfg); + + dw_spi_mask_intr(dws, 0xff); + + dw_spi_enable_chip(dws, 1); + + /* + * DW APB SSI controller has very nasty peculiarities. First originally + * (without any vendor-specific modifications) it doesn't provide a + * direct way to set and clear the native chip-select signal. Instead + * the controller asserts the CS lane if Tx FIFO isn't empty and a + * transmission is going on, and automatically de-asserts it back to + * the high level if the Tx FIFO doesn't have anything to be pushed + * out. Due to that a multi-tasking or heavy IRQs activity might be + * fatal, since the transfer procedure preemption may cause the Tx FIFO + * getting empty and sudden CS de-assertion, which in the middle of the + * transfer will most likely cause the data loss. Secondly the + * EEPROM-read or Read-only DW SPI transfer modes imply the incoming + * data being automatically pulled in into the Rx FIFO. So if the + * driver software is late in fetching the data from the FIFO before + * it's overflown, new incoming data will be lost. In order to make + * sure the executed memory operations are CS-atomic and to prevent the + * Rx FIFO overflow we have to disable the local interrupts so to block + * any preemption during the subsequent IO operations. + * + * Note. At some circumstances disabling IRQs may not help to prevent + * the problems described above. The CS de-assertion and Rx FIFO + * overflow may still happen due to the relatively slow system bus or + * CPU not working fast enough, so the write-then-read algo implemented + * here just won't keep up with the SPI bus data transfer. Such + * situation is highly platform specific and is supposed to be fixed by + * manually restricting the SPI bus frequency using the + * dws->max_mem_freq parameter. + */ + if (!enhanced_spi) { + local_irq_save(flags); + preempt_disable(); + + ret = dw_spi_write_then_read(dws, mem->spi); + + local_irq_restore(flags); + preempt_enable(); + + /* + * Wait for the operation being finished and check the controller + * status only if there hasn't been any run-time error detected. In the + * former case it's just pointless. In the later one to prevent an + * additional error message printing since any hw error flag being set + * would be due to an error detected on the data transfer. + */ + if (!ret) { + ret = dw_spi_wait_mem_op_done(dws); + if (!ret) + ret = dw_spi_ext_check_status(dws, true); + } + } else { + /* + * We donot need to disable IRQs as clock stretching will + * be enabled in enhanced mode which will prevent CS + * from being de-assert. + */ + ret = enhanced_transfer(dws, mem->spi, op); + } + + dw_spi_stop_mem_op(dws, mem->spi); + + dw_spi_free_mem_buf(dws); + + return ret; +} + +/* + * Initialize the default memory operations if a glue layer hasn't specified + * custom ones. Direct mapping operations will be preserved anyway since DW SPI + * controller doesn't have an embedded dirmap interface. Note the memory + * operations implemented in this driver is the best choice only for the DW APB + * SSI controller with standard native CS functionality. If a hardware vendor + * has fixed the automatic CS assertion/de-assertion peculiarity, then it will + * be safer to use the normal SPI-messages-based transfers implementation. + */ +static void dw_spi_init_mem_ops(struct dw_spi *dws) +{ + if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) && + !dws->set_cs) { + dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size; + dws->mem_ops.supports_op = dw_spi_supports_mem_op; + dws->mem_ops.exec_op = dw_spi_exec_mem_op; + if (!dws->max_mem_freq) + dws->max_mem_freq = dws->max_freq; + } +} + +/* This may be called twice for each spi dev */ +static int dw_spi_setup(struct spi_device *spi) +{ + struct dw_spi *dws = spi_controller_get_devdata(spi->controller); + struct dw_spi_chip_data *chip; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + if (!chip) { + struct dw_spi *dws = spi_controller_get_devdata(spi->controller); + u32 rx_sample_dly_ns; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + spi_set_ctldata(spi, chip); + /* Get specific / default rx-sample-delay */ + if (device_property_read_u32(&spi->dev, + "rx-sample-delay-ns", + &rx_sample_dly_ns) != 0) + /* Use default controller value */ + rx_sample_dly_ns = dws->def_rx_sample_dly_ns; + chip->rx_sample_dly = DIV_ROUND_CLOSEST(rx_sample_dly_ns, + NSEC_PER_SEC / + dws->max_freq); + } + + /* + * Update CR0 data each time the setup callback is invoked since + * the device parameters could have been changed, for instance, by + * the MMC SPI driver or something else. + */ + chip->cr0 = dw_spi_prepare_cr0(dws, spi); + + return 0; +} + +static void dw_spi_cleanup(struct spi_device *spi) +{ + struct dw_spi_chip_data *chip = spi_get_ctldata(spi); + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +/* Restart the controller, disable all interrupts, clean rx fifo */ +static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws) +{ + dw_spi_reset_chip(dws); + + /* + * Retrieve the Synopsys component version if it hasn't been specified + * by the platform. CoreKit version ID is encoded as a 3-chars ASCII + * code enclosed with '*' (typical for the most of Synopsys IP-cores). + */ + if (!dws->ver) { + dws->ver = dw_readl(dws, DW_SPI_VERSION); + + dev_dbg(dev, "Synopsys DWC%sSSI v%c.%c%c\n", + dw_spi_ip_is(dws, PSSI) ? " APB " : " ", + DW_SPI_GET_BYTE(dws->ver, 3), DW_SPI_GET_BYTE(dws->ver, 2), + DW_SPI_GET_BYTE(dws->ver, 1)); + } + + /* + * Try to detect the FIFO depth if not set by interface driver, + * the depth could be from 2 to 256 from HW spec + */ + if (!dws->fifo_len) { + u32 fifo; + + for (fifo = 1; fifo < 256; fifo++) { + dw_writel(dws, DW_SPI_TXFTLR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFTLR)) + break; + } + dw_writel(dws, DW_SPI_TXFTLR, 0); + + dws->fifo_len = (fifo == 1) ? 0 : fifo; + dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); + } + + /* + * Detect CTRLR0.DFS field size and offset by testing the lowest bits + * writability. Note DWC SSI controller also has the extended DFS, but + * with zero offset. + */ + if (dw_spi_ip_is(dws, PSSI)) { + u32 cr0, tmp = dw_readl(dws, DW_SPI_CTRLR0); + + dw_spi_enable_chip(dws, 0); + dw_writel(dws, DW_SPI_CTRLR0, 0xffffffff); + cr0 = dw_readl(dws, DW_SPI_CTRLR0); + dw_writel(dws, DW_SPI_CTRLR0, tmp); + dw_spi_enable_chip(dws, 1); + + if (!(cr0 & DW_PSSI_CTRLR0_DFS_MASK)) { + dws->caps |= DW_SPI_CAP_DFS32; + dws->dfs_offset = __bf_shf(DW_PSSI_CTRLR0_DFS32_MASK); + dev_dbg(dev, "Detected 32-bits max data frame size\n"); + } + } else { + dws->caps |= DW_SPI_CAP_DFS32; + } +} + +int dw_spi_ext_add_host(struct device *dev, struct dw_spi *dws) +{ + struct spi_controller *master; + int ret; + + if (!dws) + return -EINVAL; + + master = spi_alloc_master(dev, 0); + if (!master) + return -ENOMEM; + + device_set_node(&master->dev, dev_fwnode(dev)); + + dws->master = master; + dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); + + spi_controller_set_devdata(master, dws); + + /* Basic HW init */ + dw_spi_hw_init(dev, dws); + + ret = request_irq(dws->irq, dw_spi_ext_irq, IRQF_SHARED, dev_name(dev), + master); + if (ret < 0 && ret != -ENOTCONN) { + dev_err(dev, "can not get IRQ\n"); + goto err_free_master; + } + + dw_spi_init_mem_ops(dws); + + master->use_gpio_descriptors = true; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + if (dws->caps & DW_SPI_CAP_EXT_SPI) + master->mode_bits |= SPI_TX_DUAL | SPI_RX_DUAL | + SPI_TX_QUAD | SPI_RX_QUAD | + SPI_TX_OCTAL | SPI_RX_OCTAL; + if (dws->caps & DW_SPI_CAP_DFS32) + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + else + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + master->bus_num = dws->bus_num; + master->num_chipselect = dws->num_cs; + master->setup = dw_spi_setup; + master->cleanup = dw_spi_cleanup; + if (dws->set_cs) + master->set_cs = dws->set_cs; + else + master->set_cs = dw_spi_ext_set_cs; + master->transfer_one = dw_spi_transfer_one; + master->handle_err = dw_spi_handle_err; + if (dws->mem_ops.exec_op) + master->mem_ops = &dws->mem_ops; + master->max_speed_hz = dws->max_freq; + master->flags = SPI_MASTER_GPIO_SS; + master->auto_runtime_pm = true; + + /* Get default rx sample delay */ + device_property_read_u32(dev, "rx-sample-delay-ns", + &dws->def_rx_sample_dly_ns); + + if (dws->dma_ops && dws->dma_ops->dma_init) { + ret = dws->dma_ops->dma_init(dev, dws); + if (ret == -EPROBE_DEFER) { + goto err_free_irq; + } else if (ret) { + dev_warn(dev, "DMA init failed\n"); + } else { + master->can_dma = dws->dma_ops->can_dma; + master->flags |= SPI_CONTROLLER_MUST_TX; + } + } + + ret = spi_register_controller(master); + if (ret) { + dev_err_probe(dev, ret, "problem registering spi master\n"); + goto err_dma_exit; + } + + dw_spi_debugfs_init(dws); + return 0; + +err_dma_exit: + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); + dw_spi_enable_chip(dws, 0); +err_free_irq: + free_irq(dws->irq, master); +err_free_master: + spi_controller_put(master); + return ret; +} + +void dw_spi_ext_remove_host(struct dw_spi *dws) +{ + dw_spi_debugfs_remove(dws); + + spi_unregister_controller(dws->master); + + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); + + dw_spi_shutdown_chip(dws); + + free_irq(dws->irq, dws->master); +} + +int dw_spi_ext_suspend_host(struct dw_spi *dws) +{ + int ret; + + ret = spi_controller_suspend(dws->master); + if (ret) + return ret; + + dw_spi_shutdown_chip(dws); + return 0; +} + +int dw_spi_ext_resume_host(struct dw_spi *dws) +{ + dw_spi_hw_init(&dws->master->dev, dws); + return spi_controller_resume(dws->master); +} + +MODULE_AUTHOR("George hu"); +MODULE_DESCRIPTION("Spacemit enhance spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-dw-espi.h b/drivers/spi/spi-dw-espi.h new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-dw-espi.h @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef DW_ESPI_HEADER_H +#define DW_ESPI_HEADER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Synopsys DW SSI IP-core virtual IDs */ +#define DW_PSSI_ID 0 +#define DW_HSSI_ID 1 + +/* Synopsys DW SSI component versions (FourCC sequence) */ +#define DW_HSSI_102A 0x3130322a + +/* DW SSI IP-core ID and version check helpers */ +#define dw_spi_ip_is(_dws, _ip) \ + ((_dws)->ip == DW_ ## _ip ## _ID) + +#define __dw_spi_ver_cmp(_dws, _ip, _ver, _op) \ + (dw_spi_ip_is(_dws, _ip) && (_dws)->ver _op DW_ ## _ip ## _ ## _ver) + +#define dw_spi_ver_is(_dws, _ip, _ver) __dw_spi_ver_cmp(_dws, _ip, _ver, ==) + +#define dw_spi_ver_is_ge(_dws, _ip, _ver) __dw_spi_ver_cmp(_dws, _ip, _ver, >=) + +/* DW SPI controller capabilities */ +#define DW_SPI_CAP_CS_OVERRIDE BIT(0) +#define DW_SPI_CAP_DFS32 BIT(1) +#define DW_SPI_CAP_EXT_SPI BIT(2) + +/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */ +#define DW_SPI_CTRLR0 0x00 +#define DW_SPI_CTRLR1 0x04 +#define DW_SPI_SSIENR 0x08 +#define DW_SPI_MWCR 0x0c +#define DW_SPI_SER 0x10 +#define DW_SPI_BAUDR 0x14 +#define DW_SPI_TXFTLR 0x18 +#define DW_SPI_RXFTLR 0x1c +#define DW_SPI_TXFLR 0x20 +#define DW_SPI_RXFLR 0x24 +#define DW_SPI_SR 0x28 +#define DW_SPI_IMR 0x2c +#define DW_SPI_ISR 0x30 +#define DW_SPI_RISR 0x34 +#define DW_SPI_TXOICR 0x38 +#define DW_SPI_RXOICR 0x3c +#define DW_SPI_RXUICR 0x40 +#define DW_SPI_MSTICR 0x44 +#define DW_SPI_ICR 0x48 +#define DW_SPI_DMACR 0x4c +#define DW_SPI_DMATDLR 0x50 +#define DW_SPI_DMARDLR 0x54 +#define DW_SPI_IDR 0x58 +#define DW_SPI_VERSION 0x5c +#define DW_SPI_DR 0x60 +#define DW_SPI_RX_SAMPLE_DLY 0xf0 +#define DW_SPI_SPI_CTRLR0 0xf4 +#define DW_SPI_DDR_DRV_EDGE 0xf8 + +/* Bit fields in CTRLR0 (DWC APB SSI) */ +#define DW_PSSI_CTRLR0_DFS_MASK GENMASK(3, 0) +#define DW_PSSI_CTRLR0_DFS32_MASK GENMASK(20, 16) + +#define DW_PSSI_CTRLR0_FRF_MASK GENMASK(5, 4) +#define DW_SPI_CTRLR0_FRF_MOTO_SPI 0x0 +#define DW_SPI_CTRLR0_FRF_TI_SSP 0x1 +#define DW_SPI_CTRLR0_FRF_NS_MICROWIRE 0x2 +#define DW_SPI_CTRLR0_FRF_RESV 0x3 + +#define DW_PSSI_CTRLR0_MODE_MASK GENMASK(7, 6) +#define DW_PSSI_CTRLR0_SCPHA BIT(6) +#define DW_PSSI_CTRLR0_SCPOL BIT(7) + +#define DW_PSSI_CTRLR0_TMOD_MASK GENMASK(9, 8) +#define DW_SPI_CTRLR0_TMOD_TR 0x0 /* xmit & recv */ +#define DW_SPI_CTRLR0_TMOD_TO 0x1 /* xmit only */ +#define DW_SPI_CTRLR0_TMOD_RO 0x2 /* recv only */ +#define DW_SPI_CTRLR0_TMOD_EPROMREAD 0x3 /* eeprom read mode */ + +#define DW_PSSI_CTRLR0_SLV_OE BIT(10) +#define DW_PSSI_CTRLR0_SRL BIT(11) +#define DW_PSSI_CTRLR0_CFS BIT(12) + +/* Bit fields in CTRLR0 (DWC SSI with AHB interface) */ +#define DW_HSSI_CTRLR0_DFS_MASK GENMASK(4, 0) +#define DW_HSSI_CTRLR0_FRF_MASK GENMASK(7, 6) +#define DW_HSSI_CTRLR0_SCPHA BIT(8) +#define DW_HSSI_CTRLR0_SCPOL BIT(9) +#define DW_HSSI_CTRLR0_TMOD_MASK GENMASK(11, 10) +#define DW_HSSI_CTRLR0_SRL BIT(13) +#define DW_HSSI_CTRLR0_MST BIT(31) + +/* Bit fields in CTRLR0 for enhanced SPI */ +#define DW_HSSI_CTRLR0_SPI_FRF_MASK GENMASK(23, 22) +#define DW_SSI_CTRLR0_SPI_FRF_DUAL_SPI 0x1 +#define DW_SSI_CTRLR0_SPI_FRF_QUAD_SPI 0x2 +#define DW_SSI_CTRLR0_SPI_FRF_OCT_SPI 0x3 + +/* Bit fields in CTRLR1 */ +#define DW_SPI_NDF_MASK GENMASK(15, 0) + +/* Bit fields in SR, 7 bits */ +#define DW_SPI_SR_MASK GENMASK(6, 0) +#define DW_SPI_SR_BUSY BIT(0) +#define DW_SPI_SR_TF_NOT_FULL BIT(1) +#define DW_SPI_SR_TF_EMPT BIT(2) +#define DW_SPI_SR_RF_NOT_EMPT BIT(3) +#define DW_SPI_SR_RF_FULL BIT(4) +#define DW_SPI_SR_TX_ERR BIT(5) +#define DW_SPI_SR_DCOL BIT(6) + +/* Bit fields in ISR, IMR, RISR, 7 bits */ +#define DW_SPI_INT_MASK GENMASK(5, 0) +#define DW_SPI_INT_TXEI BIT(0) +#define DW_SPI_INT_TXOI BIT(1) +#define DW_SPI_INT_RXUI BIT(2) +#define DW_SPI_INT_RXOI BIT(3) +#define DW_SPI_INT_RXFI BIT(4) +#define DW_SPI_INT_MSTI BIT(5) + +/* Bit fields in DMACR */ +#define DW_SPI_DMACR_RDMAE BIT(0) +#define DW_SPI_DMACR_TDMAE BIT(1) + +/* Bit fields in SPI_CTRLR0 (Defined in DWC SSI >= 1.02a) */ +#define DW_HSSI_SPI_CTRLR0_CLK_STRETCH_EN BIT(30) +#define DW_HSSI_SPI_CTRLR0_WAIT_CYCLE_MASK GENMASK(15, 11) +#define DW_HSSI_SPI_CTRLR0_INST_L_MASK GENMASK(9, 8) +#define DW_HSSI_SPI_CTRLR0_INST_L8 0x2 +#define DW_HSSI_SPI_CTRLR0_ADDR_L_MASK GENMASK(5, 2) +#define DW_HSSI_SPI_CTRLR0_ADDR_L32 0x8 + +/* Mem/DMA operations helpers */ +#define DW_SPI_WAIT_RETRIES 5 +#define DW_SPI_BUF_SIZE \ + (sizeof_field(struct spi_mem_op, cmd.opcode) + \ + sizeof_field(struct spi_mem_op, addr.val) + 256) +#define DW_SPI_GET_BYTE(_val, _idx) \ + ((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff) + +/* Slave spi_transfer/spi_mem_op related */ +struct dw_spi_cfg { + u8 tmode; + u8 dfs; + u32 ndf; + u32 freq; + u8 spi_frf; +}; + +struct dw_spi; +struct dw_spi_dma_ops { + int (*dma_init)(struct device *dev, struct dw_spi *dws); + void (*dma_exit)(struct dw_spi *dws); + int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); + bool (*can_dma)(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *xfer); + int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer); + void (*dma_stop)(struct dw_spi *dws); +}; + +struct dw_spi { + struct spi_controller *master; + + u32 ip; /* Synopsys DW SSI IP-core ID */ + u32 ver; /* Synopsys component version */ + u32 caps; /* DW SPI capabilities */ + + void __iomem *regs; + unsigned long paddr; + int irq; + u32 fifo_len; /* depth of the FIFO buffer */ + unsigned int dfs_offset; /* CTRLR0 DFS field offset */ + u32 max_mem_freq; /* max mem-ops bus freq */ + u32 max_freq; /* max bus freq supported */ + + u32 reg_io_width; /* DR I/O width in bytes */ + u16 bus_num; + u16 num_cs; /* supported slave numbers */ + void (*set_cs)(struct spi_device *spi, bool enable); + + /* Current message transfer state info */ + void *tx; + unsigned int tx_len; + void *rx; + unsigned int rx_len; + u8 buf[DW_SPI_BUF_SIZE]; + int dma_mapped; + u8 n_bytes; /* current is a 1/2 bytes op */ + irqreturn_t (*transfer_handler)(struct dw_spi *dws); + u32 current_freq; /* frequency in hz */ + u32 cur_rx_sample_dly; + u32 def_rx_sample_dly_ns; + + /* Custom memory operations */ + struct spi_controller_mem_ops mem_ops; + + /* DMA info */ + struct dma_chan *txchan; + u32 txburst; + struct dma_chan *rxchan; + u32 rxburst; + u32 dma_sg_burst; + unsigned long dma_chan_busy; + dma_addr_t dma_addr; /* phy address of the Data register */ + const struct dw_spi_dma_ops *dma_ops; + struct completion dma_completion; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; + struct debugfs_regset32 regset; +#endif +}; + +static inline u32 dw_readl(struct dw_spi *dws, u32 offset) +{ + return __raw_readl(dws->regs + offset); +} + +static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val) +{ + __raw_writel(val, dws->regs + offset); +} + +static inline u32 dw_read_io_reg(struct dw_spi *dws, u32 offset) +{ + switch (dws->reg_io_width) { + case 2: + return readw_relaxed(dws->regs + offset); + case 4: + default: + return readl_relaxed(dws->regs + offset); + } +} + +static inline void dw_write_io_reg(struct dw_spi *dws, u32 offset, u32 val) +{ + switch (dws->reg_io_width) { + case 2: + writew_relaxed(val, dws->regs + offset); + break; + case 4: + default: + writel_relaxed(val, dws->regs + offset); + break; + } +} + +static inline void dw_spi_enable_chip(struct dw_spi *dws, int enable) +{ + dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0)); +} + +static inline void dw_spi_set_clk(struct dw_spi *dws, u16 div) +{ + dw_writel(dws, DW_SPI_BAUDR, div); +} + +/* Disable IRQ bits */ +static inline void dw_spi_mask_intr(struct dw_spi *dws, u32 mask) +{ + u32 new_mask; + + new_mask = dw_readl(dws, DW_SPI_IMR) & ~mask; + dw_writel(dws, DW_SPI_IMR, new_mask); +} + +/* Enable IRQ bits */ +static inline void dw_spi_umask_intr(struct dw_spi *dws, u32 mask) +{ + u32 new_mask; + + new_mask = dw_readl(dws, DW_SPI_IMR) | mask; + dw_writel(dws, DW_SPI_IMR, new_mask); +} + +/* + * This disables the SPI controller, interrupts, clears the interrupts status + * and CS, then re-enables the controller back. Transmit and receive FIFO + * buffers are cleared when the device is disabled. + */ +static inline void dw_spi_reset_chip(struct dw_spi *dws) +{ + dw_spi_enable_chip(dws, 0); + dw_spi_mask_intr(dws, 0xff); + dw_readl(dws, DW_SPI_ICR); + dw_writel(dws, DW_SPI_SER, 0); + dw_spi_enable_chip(dws, 1); +} + +static inline void dw_spi_shutdown_chip(struct dw_spi *dws) +{ + dw_spi_enable_chip(dws, 0); + dw_spi_set_clk(dws, 0); +} + +extern void dw_spi_ext_set_cs(struct spi_device *spi, bool enable); +extern void dw_spi_ext_update_config(struct dw_spi *dws, struct spi_device *spi, + struct dw_spi_cfg *cfg); +extern int dw_spi_ext_check_status(struct dw_spi *dws, bool raw); +extern int dw_spi_ext_add_host(struct device *dev, struct dw_spi *dws); +extern void dw_spi_ext_remove_host(struct dw_spi *dws); +extern int dw_spi_ext_suspend_host(struct dw_spi *dws); +extern int dw_spi_ext_resume_host(struct dw_spi *dws); + +#ifdef CONFIG_SPI_DW_DMA + +extern void dw_spi_dma_setup_mfld(struct dw_spi *dws); +extern void dw_spi_dma_setup_generic(struct dw_spi *dws); + +#else + +static inline void dw_spi_dma_setup_mfld(struct dw_spi *dws) {} +static inline void dw_spi_dma_setup_generic(struct dw_spi *dws) {} + +#endif /* !CONFIG_SPI_DW_DMA */ + +#endif /* DW_ESPI_HEADER_H */ diff --git a/drivers/spi/spi-dw-mmio-ext.c b/drivers/spi/spi-dw-mmio-ext.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-dw-mmio-ext.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Memory-mapped interface driver for DW SPI Core + * + * Copyright (c) 2023, spacemit. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spi-dw-espi.h" + +#define DRIVER_NAME "dw_spi_mmio_ext" + +struct dw_spi_mmio_ext { + struct dw_spi dws; + struct clk *clk; + struct clk *pclk; + void *priv; + struct reset_control *rstc; +}; + +static int dw_spi_hssi_ext_init(struct platform_device *pdev, + struct dw_spi_mmio_ext *dwsmmio) +{ + dwsmmio->dws.ip = DW_HSSI_ID; + dwsmmio->dws.caps = DW_SPI_CAP_EXT_SPI; + + dw_spi_dma_setup_generic(&dwsmmio->dws); + + return 0; +} + +static int dw_spi_mmio_ext_probe(struct platform_device *pdev) +{ + int (*init_func)(struct platform_device *pdev, + struct dw_spi_mmio_ext *dwsmmio); + struct dw_spi_mmio_ext *dwsmmio; + struct resource *mem; + struct dw_spi *dws; + int ret; + int num_cs; + + dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio_ext), + GFP_KERNEL); + if (!dwsmmio) + return -ENOMEM; + + dws = &dwsmmio->dws; + + /* Get basic io resource and map it */ + dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(dws->regs)) + return PTR_ERR(dws->regs); + + dws->paddr = mem->start; + + dws->irq = platform_get_irq(pdev, 0); + if (dws->irq < 0) + return dws->irq; /* -ENXIO */ + + dwsmmio->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dwsmmio->clk)) + return PTR_ERR(dwsmmio->clk); + ret = clk_prepare_enable(dwsmmio->clk); + if (ret) + return ret; + + /* Optional clock needed to access the registers */ + dwsmmio->pclk = devm_clk_get_optional(&pdev->dev, "pclk"); + if (IS_ERR(dwsmmio->pclk)) { + ret = PTR_ERR(dwsmmio->pclk); + goto out_clk; + } + ret = clk_prepare_enable(dwsmmio->pclk); + if (ret) + goto out_clk; + + /* find an optional reset controller */ + dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(dwsmmio->rstc)) { + ret = PTR_ERR(dwsmmio->rstc); + dev_err(&pdev->dev, "failed to get reset.\n"); + goto out_clk; + } + reset_control_deassert(dwsmmio->rstc); + + /* set bus number */ + dws->bus_num = pdev->id; + + /* get supported freq in max */ + dws->max_freq = clk_get_rate(dwsmmio->clk); + + /* get reg width of controler */ + device_property_read_u32(&pdev->dev, "reg-io-width", &dws->reg_io_width); + + /* get chip select count of controler */ + num_cs = 4; + device_property_read_u32(&pdev->dev, "num-cs", &num_cs); + dws->num_cs = num_cs; + init_func = device_get_match_data(&pdev->dev); + if (init_func) { + ret = init_func(pdev, dwsmmio); + if (ret) + goto out; + } + + pm_runtime_enable(&pdev->dev); + + ret = dw_spi_ext_add_host(&pdev->dev, dws); + if (ret) + goto out; + + platform_set_drvdata(pdev, dwsmmio); + dev_info(&pdev->dev, "dw_spi_ext_probe success.\n"); + return 0; + +out: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(dwsmmio->pclk); +out_clk: + clk_disable_unprepare(dwsmmio->clk); + reset_control_assert(dwsmmio->rstc); + + return ret; +} + +static int dw_spi_mmio_ext_remove(struct platform_device *pdev) +{ + struct dw_spi_mmio_ext *dwsmmio = platform_get_drvdata(pdev); + + dw_spi_ext_remove_host(&dwsmmio->dws); + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(dwsmmio->pclk); + clk_disable_unprepare(dwsmmio->clk); + reset_control_assert(dwsmmio->rstc); + + return 0; +} + +static const struct of_device_id dw_spi_mmio_ext_of_match[] = { + { .compatible = "snps,dwc-ssi-1.02a", .data = dw_spi_hssi_ext_init}, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, dw_spi_mmio_ext_of_match); + +static struct platform_driver dw_spi_mmio_ext_driver = { + .probe = dw_spi_mmio_ext_probe, + .remove = dw_spi_mmio_ext_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = dw_spi_mmio_ext_of_match, + }, +}; +module_platform_driver(dw_spi_mmio_ext_driver); + +MODULE_AUTHOR("George hu"); +MODULE_DESCRIPTION("Spacemit qspi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-k1x-dma.c b/drivers/spi/spi-k1x-dma.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-k1x-dma.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for Spacemit k1x spi controller dma mode + * + * Copyright (c) 2023, spacemit Corporation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "spi-k1x.h" + +static int k1x_spi_map_dma_buffer(struct spi_driver_data *drv_data, + enum dma_data_direction dir) +{ + int i, nents, len = drv_data->len; + struct scatterlist *sg; + struct device *dmadev; + struct sg_table *sgt; + void *buf, *pbuf; + + if (dir == DMA_TO_DEVICE) { + dmadev = drv_data->tx_chan->device->dev; + sgt = &drv_data->tx_sgt; + buf = drv_data->tx; + drv_data->tx_map_len = len; + } else { + dmadev = drv_data->rx_chan->device->dev; + sgt = &drv_data->rx_sgt; + buf = drv_data->rx; + drv_data->rx_map_len = len; + } + + nents = DIV_ROUND_UP(len, SZ_2K); + if (nents != sgt->nents) { + int ret; + + sg_free_table(sgt); + ret = sg_alloc_table(sgt, nents, GFP_ATOMIC); + if (ret) + return ret; + } + + pbuf = buf; + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + size_t bytes = min_t(size_t, len, SZ_2K); + + if (buf) + sg_set_buf(sg, pbuf, bytes); + else + sg_set_buf(sg, drv_data->dummy, bytes); + + pbuf += bytes; + len -= bytes; + } + + nents = dma_map_sg(dmadev, sgt->sgl, sgt->nents, dir); + if (!nents) + return -ENOMEM; + + return nents; +} + +static void k1x_spi_unmap_dma_buffer(struct spi_driver_data *drv_data, + enum dma_data_direction dir) +{ + struct device *dmadev; + struct sg_table *sgt; + + if (dir == DMA_TO_DEVICE) { + dmadev = drv_data->tx_chan->device->dev; + sgt = &drv_data->tx_sgt; + } else { + dmadev = drv_data->rx_chan->device->dev; + sgt = &drv_data->rx_sgt; + } + + dma_unmap_sg(dmadev, sgt->sgl, sgt->nents, dir); +} + +static void k1x_spi_unmap_dma_buffers(struct spi_driver_data *drv_data) +{ + if (!drv_data->dma_mapped) + return; + + k1x_spi_unmap_dma_buffer(drv_data, DMA_FROM_DEVICE); + k1x_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE); + + drv_data->dma_mapped = 0; +} + +static void k1x_spi_dma_transfer_complete(struct spi_driver_data *drv_data, + bool error) +{ + struct spi_message *msg = drv_data->cur_msg; + + /* + * It is possible that one CPU is handling ROR interrupt and other + * just gets DMA completion. Calling pump_transfers() twice for the + * same transfer leads to problems thus we prevent concurrent calls + * by using ->dma_running. + */ + if (atomic_dec_and_test(&drv_data->dma_running)) { + /* + * If the other CPU is still handling the ROR interrupt we + * might not know about the error yet. So we re-check the + * ROR bit here before we clear the status register. + */ + if (!error) { + u32 status = k1x_spi_read(drv_data, STATUS) + & drv_data->mask_sr; + error = status & STATUS_ROR; + } + + /* Clear status & disable interrupts */ + k1x_spi_write(drv_data, FIFO_CTRL, + k1x_spi_read(drv_data, FIFO_CTRL) + & ~drv_data->dma_fifo_ctrl); + k1x_spi_write(drv_data, TOP_CTRL, + k1x_spi_read(drv_data, TOP_CTRL) + & ~drv_data->dma_top_ctrl); + k1x_spi_write(drv_data, STATUS, drv_data->clear_sr); + k1x_spi_write(drv_data, TO, 0); + + if (!error) { + k1x_spi_unmap_dma_buffers(drv_data); + + drv_data->tx += drv_data->tx_map_len; + drv_data->rx += drv_data->rx_map_len; + + msg->actual_length += drv_data->len; + msg->state = k1x_spi_next_transfer(drv_data); + } else { + /* In case we got an error we disable the SSP now */ + k1x_spi_write(drv_data, TOP_CTRL, + k1x_spi_read(drv_data, TOP_CTRL) + & ~TOP_SSE); + + msg->state = ERROR_STATE; + } + queue_work(system_wq, &drv_data->pump_transfers); + } +} + +static void k1x_spi_dma_callback(void *data) +{ + k1x_spi_dma_transfer_complete(data, false); +} + +static struct dma_async_tx_descriptor * +k1x_spi_dma_prepare_one(struct spi_driver_data *drv_data, + enum dma_transfer_direction dir) +{ + struct chip_data *chip = drv_data->cur_chip; + enum dma_slave_buswidth width; + struct dma_slave_config cfg; + struct dma_chan *chan; + struct sg_table *sgt; + int nents, ret; + + switch (drv_data->n_bytes) { + case 1: + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = drv_data->ssdr_physical; + cfg.dst_addr_width = width; + cfg.dst_maxburst = chip->dma_burst_size; + + sgt = &drv_data->tx_sgt; + nents = drv_data->tx_nents; + chan = drv_data->tx_chan; + } else { + cfg.src_addr = drv_data->ssdr_physical; + cfg.src_addr_width = width; + cfg.src_maxburst = chip->dma_burst_size; + + sgt = &drv_data->rx_sgt; + nents = drv_data->rx_nents; + chan = drv_data->rx_chan; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_warn(&drv_data->pdev->dev, "DMA slave config failed\n"); + return NULL; + } + + return dmaengine_prep_slave_sg(chan, sgt->sgl, nents, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +} + +bool k1x_spi_dma_is_possible(size_t len) +{ + return len <= MAX_DMA_LEN; +} + +int k1x_spi_map_dma_buffers(struct spi_driver_data *drv_data) +{ + const struct chip_data *chip = drv_data->cur_chip; + int ret; + + if (!chip->enable_dma) + return 0; + + /* Don't bother with DMA if we can't do even a single burst */ + if (drv_data->len < chip->dma_burst_size) + return 0; + + ret = k1x_spi_map_dma_buffer(drv_data, DMA_TO_DEVICE); + if (ret <= 0) { + dev_warn(&drv_data->pdev->dev, "failed to DMA map TX\n"); + return 0; + } + + drv_data->tx_nents = ret; + + ret = k1x_spi_map_dma_buffer(drv_data, DMA_FROM_DEVICE); + if (ret <= 0) { + k1x_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE); + dev_warn(&drv_data->pdev->dev, "failed to DMA map RX\n"); + return 0; + } + + drv_data->rx_nents = ret; + return 1; +} + +irqreturn_t k1x_spi_dma_transfer(struct spi_driver_data *drv_data) +{ + u32 status; + + status = k1x_spi_read(drv_data, STATUS) & drv_data->mask_sr; + + if (((drv_data->slave_mode) && (status & STATUS_TINT)) || + (status & STATUS_ROR)) { + dmaengine_terminate_all(drv_data->rx_chan); + dmaengine_terminate_all(drv_data->tx_chan); + k1x_spi_dma_transfer_complete(drv_data, true); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +void k1x_spi_slave_sw_timeout_callback(struct spi_driver_data *drv_data) +{ + dmaengine_terminate_all(drv_data->rx_chan); + dmaengine_terminate_all(drv_data->tx_chan); + k1x_spi_dma_transfer_complete(drv_data, true); +} + +int k1x_spi_dma_prepare(struct spi_driver_data *drv_data, u32 dma_burst) +{ + struct dma_async_tx_descriptor *tx_desc, *rx_desc; + + tx_desc = k1x_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV); + if (!tx_desc) { + dev_err(&drv_data->pdev->dev, + "failed to get DMA TX descriptor\n"); + return -EBUSY; + } + + rx_desc = k1x_spi_dma_prepare_one(drv_data, DMA_DEV_TO_MEM); + if (!rx_desc) { + dev_err(&drv_data->pdev->dev, + "failed to get DMA RX descriptor\n"); + return -EBUSY; + } + + /* We are ready when RX completes */ + rx_desc->callback = k1x_spi_dma_callback; + rx_desc->callback_param = drv_data; + + dmaengine_submit(rx_desc); + dmaengine_submit(tx_desc); + return 0; +} + +void k1x_spi_dma_start(struct spi_driver_data *drv_data) +{ + dma_async_issue_pending(drv_data->rx_chan); + dma_async_issue_pending(drv_data->tx_chan); + + atomic_set(&drv_data->dma_running, 1); +} + +int k1x_spi_dma_setup(struct spi_driver_data *drv_data) +{ + struct k1x_spi_master *pdata = drv_data->master_info; + struct device *dev = &drv_data->pdev->dev; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + drv_data->dummy = devm_kzalloc(dev, SZ_2K, GFP_KERNEL); + if (!drv_data->dummy) + return -ENOMEM; + + drv_data->tx_chan = dma_request_slave_channel_compat(mask, + pdata->dma_filter, pdata->tx_param, dev, "tx"); + if (!drv_data->tx_chan) + return -ENODEV; + + drv_data->rx_chan = dma_request_slave_channel_compat(mask, + pdata->dma_filter, pdata->rx_param, dev, "rx"); + if (!drv_data->rx_chan) { + dma_release_channel(drv_data->tx_chan); + drv_data->tx_chan = NULL; + return -ENODEV; + } + + return 0; +} + +void k1x_spi_dma_release(struct spi_driver_data *drv_data) +{ + if (drv_data->rx_chan) { + dmaengine_terminate_all(drv_data->rx_chan); + dma_release_channel(drv_data->rx_chan); + sg_free_table(&drv_data->rx_sgt); + drv_data->rx_chan = NULL; + } + if (drv_data->tx_chan) { + dmaengine_terminate_all(drv_data->tx_chan); + dma_release_channel(drv_data->tx_chan); + sg_free_table(&drv_data->tx_sgt); + drv_data->tx_chan = NULL; + } +} + +int k1x_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct spi_device *spi, + u8 bits_per_word, u32 *burst_code, + u32 *threshold) +{ + /* + * If the DMA burst size is given in chip_info we use + * that, otherwise we set it to half of FIFO size; SPI + * FIFO has 16 entry, so FIFO size = 16*bits_per_word/8; + * Also we use the default FIFO thresholds for now. + */ + if (chip && chip->dma_burst_size) + *burst_code = chip->dma_burst_size; + else if (bits_per_word <= 8) { + *burst_code = 8; + } + else if (bits_per_word <= 16) + *burst_code = 16; + else + *burst_code = 32; + + *threshold = FIFO_RxTresh(RX_THRESH_DFLT) + | FIFO_TxTresh(TX_THRESH_DFLT); + + return 0; +} diff --git a/drivers/spi/spi-k1x-qspi.c b/drivers/spi/spi-k1x-qspi.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-k1x-qspi.c @@ -0,0 +1,1722 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Spacemit k1x qspi controller driver + * + * Copyright (c) 2023, spacemit Corporation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define K1X_DUMP_QSPI_REG + +#define QSPI_WAIT_TIMEOUT (300) /* ms */ +#define QSPI_AUTOSUSPEND_TIMEOUT 2000 +#define K1X_MPMU_ACGR 0xd4051024 + +/* QSPI PMUap register */ +#define PMUA_QSPI_CLK_RES_CTRL 0xd4282860 +#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 */ +#define QSPI_AMBA_BASE 0xb8000000 +#define QSPI_FLASH_A1_BASE QSPI_AMBA_BASE +#define QSPI_FLASH_A1_TOP (QSPI_FLASH_A1_BASE + 0x4000000) +#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) + +/* 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_16 + +#define QSPI_WAIT_BIT_CLEAR 0 +#define QSPI_WAIT_BIT_SET 1 + +/* QSPI Host Registers used by the driver */ +#define QSPI_MCR 0x00 +#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_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_BUF0IND 0x30 +#define QSPI_BUF1IND 0x34 +#define QSPI_BUF2IND 0x38 + +#define QSPI_SFAR 0x100 +#define QSPI_SFACR 0x104 + +#define QSPI_SMPR 0x108 +#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_FSPHS_CLK (416000000) +#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 + +/* + * 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 +}; + +/* k1x qspi host priv */ +struct k1x_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; + + u32 sfa1ad; + u32 sfa2ad; + u32 sfb1ad; + u32 sfb2ad; + + u32 pmuap_reg; + void __iomem *pmuap_addr; + u32 mpmu_acgr_reg; + void __iomem *mpmu_acgr; + + 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 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 reset_control *resets; + + struct completion cmd_completion; + struct mutex lock; + int selected; + + u32 tx_underrun_err; + u32 rx_overflow_err; + u32 ahb_overflow_err; +}; + +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 ssize_t qspi_info_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct k1x_qspi *t_qspi = dev_get_drvdata(dev); + return sprintf(buf, "%s: rx_dma_en=%d, rx_buf_size=0x%x, tx_dma_en=%d, tx_buf_size=0x%x," + "ahb_read_enable=%d, ahb_buf_size=0x%x\n", + dev_name(dev), + t_qspi->rx_dma_enable, t_qspi->rx_buf_size, + t_qspi->tx_dma_enable, t_qspi->tx_buf_size, + t_qspi->ahb_read_enable, t_qspi->ahb_buf_size); +} +static DEVICE_ATTR_RO(qspi_info); + +static ssize_t qspi_err_resp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct k1x_qspi *t_qspi = dev_get_drvdata(dev); + return sprintf(buf, "%s: tx_underrun (%d), rx_overflow (%d), ahb_overflow (%d)\n", + dev_name(dev), + t_qspi->tx_underrun_err, t_qspi->rx_overflow_err, t_qspi->ahb_overflow_err); +} +static DEVICE_ATTR_RO(qspi_err_resp); + +static struct attribute *qspi_dev_attrs[] = { + &dev_attr_qspi_info.attr, + &dev_attr_qspi_err_resp.attr, + NULL, +}; + +static struct attribute_group qspi_dev_group = { + .name = "qspi_dev", + .attrs = qspi_dev_attrs, +}; + +static void qspi_writel(struct k1x_qspi *qspi, u32 val, void __iomem *addr) +{ + if (qspi->endian_xchg) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static u32 qspi_readl(struct k1x_qspi *qspi, void __iomem *addr) +{ + if (qspi->endian_xchg) + return ioread32be(addr); + else + return ioread32(addr); +} + +static int qspi_set_func_clk(struct k1x_qspi *qspi) +{ + int ret = 0; + + 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_disable_unprepare(qspi->bus_clk); + clk_disable_unprepare(qspi->clk); + + 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; + } + + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(qspi->dev, "fail to enable clk, ret:%d\n", ret); + return ret; + } + + clk_prepare_enable(qspi->bus_clk); + + dev_dbg(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; +} + +static void qspi_config_mfp(struct k1x_qspi *qspi) +{ + int cs = qspi->cs_selected; + + /* TODO: only for FPGA */ +#if 0 + void * __iomem mfpr_base = ioremap((phys_addr_t)0xd401e000, 0x200); + if (!mfpr_base) { + pr_err("%s: ioremap mfpr reg error.\n", __func__); + return; + } + + if (cs == QSPI_CS_A1 || cs == QSPI_CS_A2) { + writel(0xa800, mfpr_base + 0x174); // QSPI_DAT3 + writel(0xa800, mfpr_base + 0x170); // QSPI_DAT2 + writel(0xa800, mfpr_base + 0x16C); // QSPI_DAT1 + writel(0xa800, mfpr_base + 0x168); // QSPI_DAT0 + writel(0xa800, mfpr_base + 0x17C); // QSPI_CLK + writel(0xc800, mfpr_base + 0x178); // QSPI_CS1 + dev_err(qspi->dev, "config mfp for cs:[%d]\n", cs); + } +#endif + dev_info(qspi->dev, "config mfp for cs:[%d]\n", cs); +} + +static int k1x_qspi_readl_poll_tout(struct k1x_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(base, reg, (reg & mask), 10, timeout_us); + else + return readl_poll_timeout(base, reg, !(reg & mask), 10, timeout_us); +} + +static void qspi_reset(struct k1x_qspi *qspi) +{ + uint32_t reg; + int err; + + /* QSPI_SR[QSPI_SR_BUSY] must be 0 */ + err = k1x_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_enter_mode(struct k1x_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 void qspi_write_sfar(struct k1x_qspi *qspi, uint32_t val) +{ + int err; + + /* QSPI_SR[IP_ACC] must be 0 */ + err = k1x_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 k1x_qspi *qspi, uint32_t val) +{ + int err; + + /* QSPI_SR[IP_ACC] must be 0 */ + err = k1x_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 k1x_qspi *qspi, int seq_id) +{ + u32 buf_cfg = 0; + + /* 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); + + buf_cfg = QSPI_BUF3CR_ALLMST_MASK | + QSPI_BUF3CR_ADATSZ((qspi->ahb_buf_size / 8)); + + /* 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 + + /* set AHB read sequence id */ + qspi_writel(qspi, QSPI_BFGENCR_SEQID(seq_id), qspi->io_map + QSPI_BFGENCR); + dev_info(qspi->dev, "AHB buf size: %d\n", qspi->ahb_buf_size); +} + +void qspi_dump_reg(struct k1x_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 k1x_qspi_invalid(struct k1x_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 void k1x_qspi_prepare_lut(struct k1x_qspi *qspi, + const struct spi_mem_op *op, u32 seq_id) +{ + u32 lutval[4] = {0,}; + int lutidx = 0; + int i; + + /* qspi cmd */ + lutval[0] |= LUT_DEF(lutidx, LUT_INSTR_CMD, + LUT_PAD(op->cmd.buswidth), + op->cmd.opcode); + lutidx++; + + /* addr bytes */ + if (op->addr.nbytes) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, 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); + lutidx++; + } + + /* read/write data bytes */ + if (op->data.nbytes) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, + op->data.dir == SPI_MEM_DATA_IN ? + LUT_INSTR_READ : LUT_INSTR_WRITE, + 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]); +} + +static void k1x_qspi_enable_interrupt(struct k1x_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 k1x_qspi_disable_interrupt(struct k1x_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 k1x_qspi_prepare_dma(struct k1x_qspi *qspi) +{ + struct dma_slave_config dma_cfg; + struct device *dev = qspi->dev; + dma_cap_mask_t mask; + + if (qspi->rx_dma_enable) { + /* RX DMA: DMA_MEMCPY type */ + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + 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_dbg(dev, "rx dma enable, channel:%d\n", qspi->rx_dma->chan_id); + } + } + + if (qspi->tx_dma_enable) { + /* TX DMA: DMA_SLAVE type */ + 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_dbg(dev, "tx dma enable, channel:%d\n", qspi->tx_dma->chan_id); + } + } else { + qspi->tx_dma_enable = 0; + } + } + + if (qspi->tx_dma || qspi->rx_dma) + init_completion(&qspi->dma_completion); +} + +static void k1x_qspi_dma_callback(void *arg) +{ + struct completion *dma_completion = arg; + + complete(dma_completion); +} + +int k1x_qspi_tx_dma_exec(struct k1x_qspi *qspi, + const struct spi_mem_op *op) +{ + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction dma_dir; + dma_cookie_t cookie; + int err = 0; + + 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 spi_controller_dma_map_mem_op_data error\n"); + return -EIO; + } + + dma_dir = DMA_MEM_TO_DEV; + 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 dmaengine_prep_slave_sg error\n"); + err = -ENOMEM; + goto out; + } + + reinit_completion(&qspi->dma_completion); + desc->callback = k1x_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: + spi_controller_dma_unmap_mem_op_data(qspi->ctrl, op, &qspi->sgt); + return err; +} + +int k1x_qspi_rx_dma_exec(struct k1x_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 = k1x_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 k1x_qspi_rx_dma_sg(struct k1x_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%pad, src:0x%pad, len:%d\n", + &dma_dst, &dma_src, len); + ret = k1x_qspi_rx_dma_exec(qspi, dma_dst, dma_src, len); + if (ret) + return ret; + dma_src += len; + } + + return 0; +} + +static int k1x_qspi_ahb_read(struct k1x_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 = k1x_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_dbg(qspi->dev, "rx dma read fallback to memcpy read.\n"); + } + + if (!qspi->rx_dma_enable || (qspi->rx_dma_enable && ret)) { + memcpy(op->data.buf.in, (qspi->ahb_map + op->addr.val), len); + } + + return 0; +} + +static int k1x_qspi_fill_txfifo(struct k1x_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)) { + 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); + } + + if (i < op->data.nbytes) { + memcpy(&val, op->data.buf.out + i, op->data.nbytes - i); + qspi_writel(qspi, val, base + QSPI_TBDR); + } + + /* + * There must be at least 128bit data available in TX FIFO + * for any pop operation otherwise QSPI_FR[TBUF] will be set + */ + for (i = op->data.nbytes; i < ALIGN_DOWN(op->data.nbytes + (QSPI_TX_BUFF_POP_MIN - 1), 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); + + /* config DMA channel and start */ + if (k1x_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 */ + k1x_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; + tbsr = qspi_readl(qspi, base + QSPI_TBSR); + while (4 * (tbsr >> 16) < min_t(unsigned int, qspi->tx_buf_size, op->data.nbytes)) { + udelay(1); + tbsr = qspi_readl(qspi, base + QSPI_TBSR); + if (wait_cnt++ >= 100) { + msleep(100); + tbsr = qspi_readl(qspi, base + QSPI_TBSR); + if (4 * (tbsr >> 16) < min_t(unsigned int, qspi->tx_buf_size, op->data.nbytes)) { + dev_err(qspi->dev, "tx dma failed to fill txbuf\n"); + /* disable all interrupts */ + qspi_writel(qspi, 0, qspi->io_map + QSPI_RSER); + dmaengine_terminate_all(qspi->tx_dma); + spi_controller_dma_unmap_mem_op_data(qspi->ctrl, op, &qspi->sgt); + qspi->tx_wmrk = 0; + + return -EIO; + } else { + break; + } + } + } + } + + return 0; +} + +static void k1x_qspi_read_rxfifo(struct k1x_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 k1x_qspi_irq_handler(int irq, void *dev_id) +{ + struct k1x_qspi *qspi = dev_id; + u32 fr; + + /* disable all interrupts */ + qspi_writel(qspi, 0, qspi->io_map + QSPI_RSER); + + fr = qspi_readl(qspi, 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) { + /* disable TBFDE interrupt */ + k1x_qspi_disable_interrupt(qspi, QSPI_RSER_TBFDE); + dev_err_ratelimited(qspi->dev, "TX buffer underrun\n"); + qspi->tx_underrun_err++; + } + if (fr & QSPI_FR_RBOF) { + dev_err(qspi->dev, "RX buffer overflow\n"); + qspi->rx_overflow_err++; + } + if (fr & QSPI_FR_ABOF) { + dev_err(qspi->dev, "AHB buffer overflow\n"); + qspi->ahb_overflow_err++; + } + } + + if (qspi->cmd_interrupt && (fr & (QSPI_FR_TFF_MASK | COMMAND_FR_FLAG | BUFFER_FR_FLAG))) + complete(&qspi->cmd_completion); + + return IRQ_HANDLED; +} + +static int k1x_qspi_do_op(struct k1x_qspi *qspi, const struct spi_mem_op *op) +{ + void __iomem *base = qspi->io_map; + int err = 0; + u32 mcr; + + if (qspi->cmd_interrupt) { + k1x_qspi_enable_interrupt(qspi, QSPI_RSER_TFIE | BUFFER_ERROR_INT | COMMAND_ERROR_INT); + init_completion(&qspi->cmd_completion); + } + +#ifdef K1X_DUMP_QSPI_REG + /* dump reg if need */ + qspi_dump_reg(qspi); +#endif + /* trigger LUT */ + qspi_writel(qspi, op->data.nbytes | QSPI_IPCR_SEQID(SEQID_LUT_SHARED_ID), + base + QSPI_IPCR); + + /* wait for the transaction complete */ + if (qspi->cmd_interrupt) { + wait_for_completion(&qspi->cmd_completion); + } else { + err = k1x_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", K1X_MPMU_ACGR, qspi_readl(qspi, qspi->mpmu_acgr)); + qspi_dump_reg(qspi); + goto tx_dma_unmap; + } + + err = k1x_qspi_readl_poll_tout(qspi, base + QSPI_SR, QSPI_SR_BUSY, + QSPI_WAIT_TIMEOUT*1000, QSPI_WAIT_BIT_CLEAR); + if (err) { + dev_err(qspi->dev, "opcode:0x%x busy timeout, ret:%d\n", op->cmd.opcode, err); + goto tx_dma_unmap; + } + + /* read RX buffer for IP command read */ + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN) { +#ifdef K1X_DUMP_QSPI_REG + qspi_dump_reg(qspi); +#endif + k1x_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); + + /* reduce tx unit size and retry */ + if (qspi->tx_dma_enable) + qspi->tx_unit_size = qspi->tx_buf_size; + + err = -EAGAIN; + } else { + if (qspi->tx_dma_enable) + qspi->tx_unit_size = qspi->tx_buf_size; + } + +tx_dma_unmap: + if (qspi->tx_wmrk) { + /* disable TBFDE interrupt and dma unmap */ + k1x_qspi_disable_interrupt(qspi, QSPI_RSER_TBFDE); + spi_controller_dma_unmap_mem_op_data(qspi->ctrl, op, &qspi->sgt); + qspi->tx_wmrk = 0; + } + + return err; +} + +static void dump_spi_mem_op_info(struct k1x_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 is_read_from_cache_opcode(u8 opcode) +{ + int ret; + + ret = ((opcode == READ_FROM_CACHE_OP) || + (opcode == READ_FROM_CACHE_OP_Fast) || + (opcode == READ_FROM_CACHE_OP_X2) || + (opcode == READ_FROM_CACHE_OP_X4) || + (opcode == READ_FROM_CACHE_OP_DUALIO) || + (opcode == READ_FROM_CACHE_OP_QUADIO)); + + return ret; +} + +static int k1x_qspi_check_buswidth(struct k1x_qspi *qspi, u8 width) +{ + switch (width) { + case 1: + case 2: + case 4: + return 0; + } + + return -ENOTSUPP; +} + +static bool k1x_qspi_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct k1x_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + int ret; + + mutex_lock(&qspi->lock); + ret = k1x_qspi_check_buswidth(qspi, op->cmd.buswidth); + + if (op->addr.nbytes) + ret |= k1x_qspi_check_buswidth(qspi, op->addr.buswidth); + + if (op->dummy.nbytes) + ret |= k1x_qspi_check_buswidth(qspi, op->dummy.buswidth); + + if (op->data.nbytes) + ret |= k1x_qspi_check_buswidth(qspi, op->data.buswidth); + + if (ret) { + mutex_unlock(&qspi->lock); + return false; + } + + /* address bytes should be equal to or less than 4 bytes */ + if (op->addr.nbytes > 4) { + mutex_unlock(&qspi->lock); + 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)))) { + mutex_unlock(&qspi->lock); + return false; + } + + if (op->data.dir == SPI_MEM_DATA_OUT && op->data.nbytes > qspi->tx_unit_size) { + mutex_unlock(&qspi->lock); + 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("k1x_qspi_supports_op: addr.val:%lld greater than the map size\n", op->addr.val); + mutex_unlock(&qspi->lock); + return false; + } + + /* number of dummy clock cycles should be <= 64 cycles */ + if (op->dummy.buswidth && + (op->dummy.nbytes * 8 / op->dummy.buswidth > 64)) { + mutex_unlock(&qspi->lock); + return false; + } + + mutex_unlock(&qspi->lock); + return true; +} + +static int k1x_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct k1x_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + int err = 0; + u32 mask; + u32 reg; + void __iomem *base; + + base = qspi->io_map; + + 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 = k1x_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", K1X_MPMU_ACGR, qspi_readl(qspi, 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; + + /* + * read page command 13h must be done by IP command. + * read from cache through the AHB bus by accessing the mapped memory. + * In all other cases we use IP commands to access the flash. + */ + if (op->data.nbytes > (qspi->rx_buf_size - 4) && + op->data.dir == SPI_MEM_DATA_IN && + qspi->ahb_read_enable && + is_read_from_cache_opcode(op->cmd.opcode)) { + k1x_qspi_prepare_lut(qspi, op, SEQID_LUT_AHBREAD_ID); + err = k1x_qspi_ahb_read(qspi, op); + } else { + /* IP command */ + k1x_qspi_prepare_lut(qspi, op, SEQID_LUT_SHARED_ID); + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) { + err = k1x_qspi_fill_txfifo(qspi, op); + } + if (!err) + err = k1x_qspi_do_op(qspi, op); + } + + /* invalidate the data in the AHB buffer. */ + k1x_qspi_invalid(qspi); + + mutex_unlock(&qspi->lock); + + return err; +} + +static int k1x_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + struct k1x_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + + mutex_lock(&qspi->lock); + 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; + } + mutex_unlock(&qspi->lock); + + return 0; +} + +static int k1x_qspi_host_init(struct k1x_qspi *qspi) +{ + void __iomem *base = qspi->io_map; + u32 reg; + + qspi->resets = devm_reset_control_array_get_optional_exclusive(qspi->dev); + if (IS_ERR(qspi->resets)) { + dev_err(qspi->dev, "Failed to get qspi's resets\n"); + return PTR_ERR(qspi->resets); + } + + /* config mfp */ + qspi_config_mfp(qspi); + + reset_control_assert(qspi->resets); + /* set PMUap */ + qspi_set_func_clk(qspi); + reset_control_deassert(qspi->resets); + + /* rest qspi */ + qspi_reset(qspi); + + /* clock settings */ + qspi_enter_mode(qspi, QSPI_DISABLE_MODE); + + /* sampled by sfif_clk_b; half cycle delay; */ + if (qspi->max_hz < (QSPI_SMPR_FSPHS_CLK >> 2)) + qspi_writel(qspi, 0x0, base + QSPI_SMPR); + else + qspi_writel(qspi, QSPI_SMPR_FSPHS_MASK, base + QSPI_SMPR); + + /* Fix wirte failure issue*/ + qspi_writel(qspi, 0x8, 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; + 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); + + /* clear all interrupt status */ + qspi_writel(qspi, 0xffffffff, base + QSPI_FR); + + dev_dbg(qspi->dev, "qspi host init done.\n"); +#ifdef K1X_DUMP_QSPI_REG + qspi_dump_reg(qspi); +#endif + return 0; +} + +static const struct spi_controller_mem_ops k1x_qspi_mem_ops = { + .adjust_op_size = k1x_qspi_adjust_op_size, + .supports_op = k1x_qspi_supports_op, + .exec_op = k1x_qspi_exec_op, +}; + +static int k1x_qspi_probe(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct k1x_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 k1x_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 frequency */ + if (of_property_read_u32(dev->of_node, "k1x,qspi-freq", &qspi->max_hz)) { + dev_err(dev, "failed to get qspi frequency\n"); + goto err_put_ctrl; + } + + /* 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, "k1x,qspi-sfa1ad", &qspi->sfa1ad)) + qspi->sfa1ad = QSPI_FLASH_A1_TOP; + else + qspi->sfa1ad += qspi->memmap_base; + if (of_property_read_u32(dev->of_node, "k1x,qspi-sfa2ad", &qspi->sfa2ad)) + qspi->sfa2ad = QSPI_FLASH_A2_TOP; + else + qspi->sfa2ad += qspi->sfa1ad; + if (of_property_read_u32(dev->of_node, "k1x,qspi-sfb1ad", &qspi->sfb1ad)) + qspi->sfb1ad = QSPI_FLASH_B1_TOP; + else + qspi->sfb1ad = qspi->sfa2ad; + if (of_property_read_u32(dev->of_node, "k1x,qspi-sfb2ad", &qspi->sfb2ad)) + qspi->sfb2ad = QSPI_FLASH_B2_TOP; + else + qspi->sfb2ad += qspi->sfb1ad; + + dev_dbg(dev, "k1x_qspi_probe:memmap base:0x%pa, memmap size:0x%x\n", + &qspi->memmap_base, qspi->memmap_size); + + 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, k1x_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_dbg(qspi->dev, "k1x_qspi_probe: host_irq:%d\n", host_irq); + + /* map QSPI PMUap register address */ + if (of_property_read_u32(dev->of_node, "k1x,qspi-pmuap-reg", &qspi->pmuap_reg)) { + qspi->pmuap_reg = PMUA_QSPI_CLK_RES_CTRL; + } + qspi->pmuap_addr = ioremap(qspi->pmuap_reg, 4); + + /* map QSPI MPMU ACGR register address */ + if (of_property_read_u32(dev->of_node, "k1x,qspi-mpmu-acgr-reg", &qspi->mpmu_acgr_reg)) { + qspi->mpmu_acgr_reg = K1X_MPMU_ACGR; + } + qspi->mpmu_acgr = ioremap(qspi->mpmu_acgr_reg, 4); + + if (of_property_read_u32(dev->of_node, "k1x,qspi-rx-buf", &qspi->rx_buf_size)) { + qspi->rx_buf_size = QSPI_RX_BUFF_MAX; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-tx-buf", &qspi->tx_buf_size)) { + qspi->tx_buf_size = QSPI_TX_BUFF_MAX; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-ahb-buf", &qspi->ahb_buf_size)) { + qspi->ahb_buf_size = QSPI_AHB_BUFF_MAX_SIZE; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-ahb-enable", &qspi->ahb_read_enable)) { + qspi->ahb_read_enable = 1; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-interrupt", &qspi->cmd_interrupt)) { + qspi->cmd_interrupt = 1; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-endian-xchg", &qspi->endian_xchg)) { + qspi->endian_xchg = 0; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-cs", &qspi->cs_selected)) { + qspi->cs_selected = QSPI_DEFAULT_CS; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-tx-dma", &qspi->tx_dma_enable)) { + qspi->tx_dma_enable = 0; + } + + if (of_property_read_u32(dev->of_node, "k1x,qspi-rx-dma", &qspi->rx_dma_enable)) { + qspi->rx_dma_enable = 0; + } + + k1x_qspi_prepare_dma(qspi); + mutex_init(&qspi->lock); + + /* set the qspi device default index */ + if (of_property_read_u32(dev->of_node, "k1x,qspi-id", &qspi_bus_num)) + ctlr->bus_num = 0; + else + ctlr->bus_num = qspi_bus_num; + ctlr->num_chipselect = 1; + ctlr->mem_ops = &k1x_qspi_mem_ops; + + dev_dbg(dev, "k1x_qspi_probe: rx_buf_size:%d, tx_buf_size:%d\n", + qspi->rx_buf_size, qspi->tx_buf_size); + dev_dbg(dev, "k1x_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 = qspi->tx_buf_size; + 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; + k1x_qspi_host_init(qspi); + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, QSPI_AUTOSUSPEND_TIMEOUT); + pm_suspend_ignore_children(&pdev->dev, 1); + pm_runtime_enable(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + + ctlr->dev.of_node = np; + ctlr->dev.parent = &pdev->dev; + ctlr->use_gpio_descriptors = true; + ctlr->auto_runtime_pm = true; + ret = spi_register_controller(ctlr); + if (ret) + goto err_destroy_mutex; + + pm_runtime_put_autosuspend(&pdev->dev); + +#ifdef CONFIG_SYSFS + ret = sysfs_create_group(&(pdev->dev.kobj), + (const struct attribute_group *)(&qspi_dev_group)); + if (ret) { + dev_err(dev, + "failed to create attr group for qspi dev!\n"); + goto err_destroy_mutex; + } +#endif + + return 0; + +err_destroy_mutex: + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + mutex_destroy(&qspi->lock); + iounmap(qspi->pmuap_addr); + +err_put_ctrl: + spi_controller_put(ctlr); + + dev_err(dev, "K1X QSPI probe failed\n"); + return ret; +} + +static int k1x_qspi_remove(struct platform_device *pdev) +{ + struct k1x_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); + + 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); + + reset_control_assert(qspi->resets); + clk_disable_unprepare(qspi->clk); + clk_disable_unprepare(qspi->bus_clk); + +#ifdef CONFIG_SYSFS + sysfs_remove_group(&(pdev->dev.kobj), + (const struct attribute_group *)(&qspi_dev_group)); +#endif + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int k1x_qspi_suspend(struct device *dev) +{ + int ret; + u32 sr; + struct k1x_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 k1x_qspi_resume(struct device *dev) +{ + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "failed to resume(ret:%d)\n", ret); + return ret; + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int k1x_qspi_runtime_suspend(struct device *dev) +{ + u32 sr; + struct k1x_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); + + return 0; +} + +static int k1x_qspi_runtime_resume(struct device *dev) +{ + struct k1x_qspi *qspi = dev_get_drvdata(dev); + + qspi_enter_mode(qspi, QSPI_NORMAL_MODE); + + return 0; +} + +static const struct dev_pm_ops k1x_qspi_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(k1x_qspi_suspend, k1x_qspi_resume) + SET_RUNTIME_PM_OPS(k1x_qspi_runtime_suspend, + k1x_qspi_runtime_resume, NULL) +}; + +#define K1X_QSPI_PMOPS (&k1x_qspi_pmops) + +#else +#define K1X_QSPI_PMOPS NULL +#endif + +static const struct of_device_id k1x_qspi_dt_ids[] = { + { .compatible = "spacemit,k1x-qspi", }, + {} +}; +MODULE_DEVICE_TABLE(of, k1x_qspi_dt_ids); + +static struct platform_driver k1x_qspi_driver = { + .driver = { + .name = "k1x-qspi", + .of_match_table = k1x_qspi_dt_ids, + .pm = K1X_QSPI_PMOPS, + }, + .probe = k1x_qspi_probe, + .remove = k1x_qspi_remove, +}; +module_platform_driver(k1x_qspi_driver); + +MODULE_AUTHOR("Spacemit"); +MODULE_DESCRIPTION("Spacemit k1x qspi controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-k1x.c b/drivers/spi/spi-k1x.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-k1x.c @@ -0,0 +1,1189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for Spacemit k1x spi controller + * + * Copyright (c) 2023, spacemit Corporation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-k1x.h" + +#define TIMOUT_DFLT 3000 +#define TIMOUT_DFLT_SLAVE 0x40000 + +//#define CONFIG_K1X_SSP_DEBUG 1 + +static bool k1x_spi_txfifo_full(const struct spi_driver_data *drv_data) +{ + return !(k1x_spi_read(drv_data, STATUS) & STATUS_TNF); +} + +static u32 k1x_configure_topctrl(const struct spi_driver_data *drv_data, u8 bits) +{ + /* + * set Motorola Frame Format + * set DSS + */ + return TOP_FRF_Motorola | TOP_DSS(bits); +} + +static void set_dvfm_constraint(struct spi_driver_data *drv_data) +{ +#if 0 + if (drv_data->qos_idle_value != PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE) + freq_qos_update_request(&drv_data->qos_idle, + drv_data->qos_idle_value); +#endif +} + +static void unset_dvfm_constraint(struct spi_driver_data *drv_data) +{ +#if 0 + if (drv_data->qos_idle_value != PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE) + freq_qos_update_request(&drv_data->qos_idle, + PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); +#endif +} + +static void init_dvfm_constraint(struct spi_driver_data *drv_data) +{ +#if 0 +#ifdef CONFIG_PM + struct freq_constraints *idle_qos; + + idle_qos = cpuidle_get_constraints(); + + freq_qos_add_request(idle_qos, &drv_data->qos_idle, FREQ_QOS_MAX, + PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE); +#endif +#endif +} + +static void deinit_dvfm_constraint(struct spi_driver_data *drv_data) +{ +#if 0 +#ifdef CONFIG_PM + freq_qos_remove_request(&drv_data->qos_idle); +#endif +#endif +} + +static void cs_assert(struct spi_driver_data *drv_data) +{ + struct chip_data *chip = drv_data->cur_chip; + + if (chip->cs_control) { + chip->cs_control(K1X_CS_ASSERT); + return; + } + + if (gpio_is_valid(chip->gpio_cs)) { + gpio_set_value(chip->gpio_cs, chip->gpio_cs_inverted); + return; + } +} + +static void cs_deassert(struct spi_driver_data *drv_data) +{ + struct chip_data *chip = drv_data->cur_chip; + + if (chip->cs_control) { + chip->cs_control(K1X_CS_DEASSERT); + return; + } + + if (gpio_is_valid(chip->gpio_cs)) { + gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted); + return; + } +} + +/* clear all rx fifo useless data */ +int k1x_spi_flush(struct spi_driver_data *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + + do { + while (k1x_spi_read(drv_data, STATUS) & STATUS_RNE) + k1x_spi_read(drv_data, DATAR); + } while ((k1x_spi_read(drv_data, STATUS) & STATUS_BSY) && --limit); + k1x_spi_write(drv_data, STATUS, STATUS_ROR); + + return limit; +} + +static int null_writer(struct spi_driver_data *drv_data) +{ + u8 n_bytes = drv_data->n_bytes; + + if (k1x_spi_txfifo_full(drv_data) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + k1x_spi_write(drv_data, DATAR, 0); + drv_data->tx += n_bytes; + + return 1; +} + +static int null_reader(struct spi_driver_data *drv_data) +{ + u8 n_bytes = drv_data->n_bytes; + + while ((k1x_spi_read(drv_data, STATUS) & STATUS_RNE) + && (drv_data->rx < drv_data->rx_end)) { + k1x_spi_read(drv_data, DATAR); + drv_data->rx += n_bytes; + } + + return drv_data->rx == drv_data->rx_end; +} + +static int u8_writer(struct spi_driver_data *drv_data) +{ + if (k1x_spi_txfifo_full(drv_data) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + k1x_spi_write(drv_data, DATAR, *(u8 *)(drv_data->tx)); + ++drv_data->tx; + + return 1; +} + +static int u8_reader(struct spi_driver_data *drv_data) +{ + while ((k1x_spi_read(drv_data, STATUS) & STATUS_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u8 *)(drv_data->rx) = k1x_spi_read(drv_data, DATAR); + ++drv_data->rx; + } + + return drv_data->rx == drv_data->rx_end; +} + +static int u16_writer(struct spi_driver_data *drv_data) +{ + if (k1x_spi_txfifo_full(drv_data) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + k1x_spi_write(drv_data, DATAR, *(u16 *)(drv_data->tx)); + drv_data->tx += 2; + + return 1; +} + +static int u16_reader(struct spi_driver_data *drv_data) +{ + while ((k1x_spi_read(drv_data, STATUS) & STATUS_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u16 *)(drv_data->rx) = k1x_spi_read(drv_data, DATAR); + drv_data->rx += 2; + } + + return drv_data->rx == drv_data->rx_end; +} + +static int u32_writer(struct spi_driver_data *drv_data) +{ + if (k1x_spi_txfifo_full(drv_data) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + k1x_spi_write(drv_data, DATAR, *(u32 *)(drv_data->tx)); + drv_data->tx += 4; + + return 1; +} + +static int u32_reader(struct spi_driver_data *drv_data) +{ + while ((k1x_spi_read(drv_data, STATUS) & STATUS_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u32 *)(drv_data->rx) = k1x_spi_read(drv_data, DATAR); + drv_data->rx += 4; + } + + return drv_data->rx == drv_data->rx_end; +} + +void *k1x_spi_next_transfer(struct spi_driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct spi_transfer *trans = drv_data->cur_transfer; + + /* Move to next transfer */ + if (trans->transfer_list.next != &msg->transfers) { + drv_data->cur_transfer = + list_entry(trans->transfer_list.next, + struct spi_transfer, + transfer_list); + return RUNNING_STATE; + } else + return DONE_STATE; +} + +/* caller already set message->status; dma and pio irqs are blocked */ +static void giveback(struct spi_driver_data *drv_data) +{ + struct spi_transfer* last_transfer; + struct spi_message *msg; + + msg = drv_data->cur_msg; + drv_data->cur_msg = NULL; + drv_data->cur_transfer = NULL; + + last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, + transfer_list); + + /* Delay if requested before any change in chip select */ + spi_transfer_delay_exec(last_transfer); + + /* Drop chip select UNLESS cs_change is true or we are returning + * a message with an error, or next message is for another chip + */ + if (!last_transfer->cs_change) + cs_deassert(drv_data); + else { + struct spi_message *next_msg; + + /* Holding of cs was hinted, but we need to make sure + * the next message is for the same chip. Don't waste + * time with the following tests unless this was hinted. + * + * We cannot postpone this until pump_messages, because + * after calling msg->complete (below) the driver that + * sent the current message could be unloaded, which + * could invalidate the cs_control() callback... + */ + + /* get a pointer to the next message, if any */ + next_msg = spi_get_next_queued_message(drv_data->master); + + /* see if the next and current messages point + * to the same chip + */ + if (next_msg && next_msg->spi != msg->spi) + next_msg = NULL; + if (!next_msg || msg->state == ERROR_STATE) + cs_deassert(drv_data); + } + + drv_data->cur_chip = NULL; + spi_finalize_current_message(drv_data->master); + unset_dvfm_constraint(drv_data); + + if (drv_data->slave_mode) + del_timer(&drv_data->slave_rx_timer); + complete(&drv_data->cur_msg_completion); +} + +static void reset_fifo_ctrl(struct spi_driver_data *drv_data) +{ + struct chip_data *chip = drv_data->cur_chip; + u32 fifo_ctrl = 0; + + fifo_ctrl |= chip->threshold; + k1x_spi_write(drv_data, FIFO_CTRL, fifo_ctrl); +} + +static void reset_int_en(struct spi_driver_data *drv_data) +{ + u32 int_en = 0; + + int_en = k1x_spi_read(drv_data, INT_EN); + int_en &= ~drv_data->int_cr; + k1x_spi_write(drv_data, INT_EN, int_en); +} + +static void int_error_stop(struct spi_driver_data *drv_data, const char* msg) +{ + /* Stop and reset SSP */ + k1x_spi_write(drv_data, STATUS, drv_data->clear_sr); + reset_fifo_ctrl(drv_data); + reset_int_en(drv_data); + k1x_spi_write(drv_data, TO, 0); + k1x_spi_flush(drv_data); + k1x_spi_write(drv_data, TOP_CTRL, + k1x_spi_read(drv_data, TOP_CTRL) & ~(TOP_SSE | TOP_HOLD_FRAME_LOW)); + dev_err(&drv_data->pdev->dev, "%s\n", msg); + + drv_data->cur_msg->state = ERROR_STATE; + queue_work(system_wq, &drv_data->pump_transfers); +} + +static void int_transfer_complete(struct spi_driver_data *drv_data) +{ + /* Stop SSP */ + k1x_spi_write(drv_data, STATUS, drv_data->clear_sr); + reset_fifo_ctrl(drv_data); + reset_int_en(drv_data); + k1x_spi_write(drv_data, TO, 0); + + /* Update total byte transferred return count actual bytes read */ + drv_data->cur_msg->actual_length += drv_data->len - + (drv_data->rx_end - drv_data->rx); + + /* Transfer delays and chip select release are + * handled in pump_transfers or giveback + */ + + /* Move to next transfer */ + drv_data->cur_msg->state = k1x_spi_next_transfer(drv_data); + + /* Schedule transfer tasklet */ + queue_work(system_wq, &drv_data->pump_transfers); +} + +static irqreturn_t interrupt_transfer(struct spi_driver_data *drv_data) +{ + u32 irq_mask = (k1x_spi_read(drv_data, INT_EN) & INT_EN_TIE) ? + drv_data->mask_sr : drv_data->mask_sr & ~STATUS_TFS; + + u32 irq_status = k1x_spi_read(drv_data, STATUS) & irq_mask; + + if (irq_status & STATUS_ROR) { + int_error_stop(drv_data, "interrupt_transfer: fifo overrun"); + return IRQ_HANDLED; + } + + if (irq_status & STATUS_TINT) { + k1x_spi_write(drv_data, STATUS, STATUS_TINT); + if (drv_data->read(drv_data)) { + int_transfer_complete(drv_data); + return IRQ_HANDLED; + } + } + + /* Drain rx fifo, Fill tx fifo and prevent overruns */ + do { + if (drv_data->read(drv_data)) { + int_transfer_complete(drv_data); + return IRQ_HANDLED; + } + } while (drv_data->write(drv_data)); + + if (drv_data->read(drv_data)) { + int_transfer_complete(drv_data); + return IRQ_HANDLED; + } + + if (drv_data->tx == drv_data->tx_end) { + u32 int_en; + + int_en = k1x_spi_read(drv_data, INT_EN); + int_en &= ~INT_EN_TIE; + + k1x_spi_write(drv_data, INT_EN, int_en); + } + + /* We did something */ + return IRQ_HANDLED; +} + +static irqreturn_t ssp_int(int irq, void *dev_id) +{ + struct spi_driver_data *drv_data = dev_id; + u32 int_en; + u32 mask = drv_data->mask_sr; + u32 int_status; + + /* + * The IRQ might be shared with other peripherals so we must first + * check that are we RPM suspended or not. If we are we assume that + * the IRQ was not for us (we shouldn't be RPM suspended when the + * interrupt is enabled). + */ + if (pm_runtime_suspended(&drv_data->pdev->dev)) + return IRQ_NONE; + + /* + * If the device is not yet in RPM suspended state and we get an + * interrupt that is meant for another device, check if status bits + * are all set to one. That means that the device is already + * powered off. + */ + int_status = k1x_spi_read(drv_data, STATUS); + if (int_status == ~0) + return IRQ_NONE; + + int_en = k1x_spi_read(drv_data, INT_EN); + + /* Ignore possible writes if we don't need to write */ + if (!(int_en & INT_EN_TIE)) + mask &= ~STATUS_TFS; + + /* Ignore RX timeout interrupt if it is disabled */ + if (!(int_en & INT_EN_TINTE)) + mask &= ~STATUS_TINT; + + if (!(int_status & mask)) + return IRQ_NONE; + + if (!drv_data->cur_msg) { + + k1x_spi_write(drv_data, TOP_CTRL, + k1x_spi_read(drv_data, TOP_CTRL) + & ~(TOP_SSE | TOP_HOLD_FRAME_LOW)); + k1x_spi_write(drv_data, INT_EN, + k1x_spi_read(drv_data, INT_EN) + & ~drv_data->int_cr); + k1x_spi_write(drv_data, TO, 0); + k1x_spi_write(drv_data, STATUS, drv_data->clear_sr); + + dev_err(&drv_data->pdev->dev, + "bad message state in interrupt handler\n"); + + /* Never fail */ + return IRQ_HANDLED; + } + + return drv_data->transfer_handler(drv_data); +} + +static void slave_rx_timer_expired(struct timer_list *t) { + struct spi_driver_data *drv_data = from_timer(drv_data, t, slave_rx_timer); +#ifdef CONFIG_K1X_SSP_DEBUG + pr_err("%s\n", __func__); + pr_err("spi top = 0x%x\n", k1x_spi_read(drv_data, TOP_CTRL)); + pr_err("fifo = 0x%x\n", k1x_spi_read(drv_data, FIFO_CTRL)); + pr_err("int_en = 0x%x\n", k1x_spi_read(drv_data, INT_EN)); + pr_err("to = 0x%x\n", k1x_spi_read(drv_data, TO)); +#endif + k1x_spi_slave_sw_timeout_callback(drv_data); +} + +static void pump_transfers(struct work_struct *work) +{ + struct spi_driver_data *drv_data = container_of(work, struct spi_driver_data, pump_transfers); + struct spi_message *message = NULL; + struct spi_transfer *transfer = NULL; + struct spi_transfer *previous = NULL; + struct chip_data *chip = NULL; + u8 bits = 0; + u32 top_ctrl; + u32 fifo_ctrl; + u32 int_en = 0; + u32 dma_thresh = drv_data->cur_chip->dma_threshold; + u32 dma_burst = drv_data->cur_chip->dma_burst_size; + + if (drv_data->slave_mode) + mod_timer(&drv_data->slave_rx_timer, + jiffies + msecs_to_jiffies(1000)); + + /* Get current state information */ + message = drv_data->cur_msg; + transfer = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (message->state == ERROR_STATE) { + message->status = -EIO; + giveback(drv_data); + return; + } + + /* Handle end of message */ + if (message->state == DONE_STATE) { + message->status = 0; + giveback(drv_data); + return; + } + + /* Delay if requested at end of transfer before CS change */ + if (message->state == RUNNING_STATE) { + previous = list_entry(transfer->transfer_list.prev, + struct spi_transfer, + transfer_list); + spi_transfer_delay_exec(previous); + + /* Drop chip select only if cs_change is requested */ + if (previous->cs_change) + cs_deassert(drv_data); + } + + /* Check if we can DMA this transfer */ + if (!k1x_spi_dma_is_possible(transfer->len) && chip->enable_dma) { + /* reject already-mapped transfers; PIO won't always work */ + if (message->is_dma_mapped + || transfer->rx_dma || transfer->tx_dma) { + dev_err(&drv_data->pdev->dev, + "pump_transfers: mapped transfer length of " + "%u is greater than %d\n", + transfer->len, MAX_DMA_LEN); + message->status = -EINVAL; + giveback(drv_data); + return; + } + + /* warn ... we force this to PIO mode */ + dev_warn_ratelimited(&message->spi->dev, + "pump_transfers: DMA disabled for transfer length %ld " + "greater than %d\n", + (long)drv_data->len, MAX_DMA_LEN); + } + + /* Setup the transfer state based on the type of transfer */ + if (k1x_spi_flush(drv_data) == 0) { + dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n"); + message->status = -EIO; + giveback(drv_data); + return; + } + drv_data->n_bytes = chip->n_bytes; + drv_data->tx = (void *)transfer->tx_buf; + drv_data->tx_end = drv_data->tx + transfer->len; + drv_data->rx = transfer->rx_buf; + drv_data->rx_end = drv_data->rx + transfer->len; + drv_data->rx_dma = transfer->rx_dma; + drv_data->tx_dma = transfer->tx_dma; + drv_data->len = transfer->len; + drv_data->write = drv_data->tx ? chip->write : null_writer; + drv_data->read = drv_data->rx ? chip->read : null_reader; + + /* Change speed and bit per word on a per transfer */ + bits = transfer->bits_per_word; + + if (bits <= 8) { + drv_data->n_bytes = 1; + drv_data->read = drv_data->read != null_reader ? + u8_reader : null_reader; + drv_data->write = drv_data->write != null_writer ? + u8_writer : null_writer; + } else if (bits <= 16) { + drv_data->n_bytes = 2; + drv_data->read = drv_data->read != null_reader ? + u16_reader : null_reader; + drv_data->write = drv_data->write != null_writer ? + u16_writer : null_writer; + } else if (bits <= 32) { + drv_data->n_bytes = 4; + drv_data->read = drv_data->read != null_reader ? + u32_reader : null_reader; + drv_data->write = drv_data->write != null_writer ? + u32_writer : null_writer; + } + /* + * if bits/word is changed in dma mode, then must check the + * thresholds and burst also + */ + if (chip->enable_dma) { + if (k1x_spi_set_dma_burst_and_threshold(chip, + message->spi, + bits, &dma_burst, + &dma_thresh)) + dev_warn_ratelimited(&message->spi->dev, + "pump_transfers: DMA burst size reduced to match bits_per_word\n"); + } + + top_ctrl = k1x_configure_topctrl(drv_data, bits); + dev_dbg(&message->spi->dev, "%u Hz, %s\n", + drv_data->master->max_speed_hz, + chip->enable_dma ? "DMA" : "PIO"); + top_ctrl |= chip->top_ctrl; + fifo_ctrl = chip->fifo_ctrl; + + if (drv_data->ssp_enhancement) { + /* + * If transfer length is times of 4, then use + * 32 bit fifo width with endian swap support + */ + if (drv_data->len % 4 == 0 && transfer->bits_per_word <= 16) { + if (transfer->bits_per_word <= 8) + fifo_ctrl |= FIFO_WR_ENDIAN_8BITS | + FIFO_RD_ENDIAN_8BITS; + else if (transfer->bits_per_word <= 16) + fifo_ctrl |= FIFO_WR_ENDIAN_16BITS | + FIFO_RD_ENDIAN_16BITS; + bits = 32; + drv_data->n_bytes = 4; + if(transfer->rx_buf) + drv_data->read = u32_reader; + if(transfer->tx_buf) + drv_data->write = u32_writer; + + if (chip->enable_dma) { + if (k1x_spi_set_dma_burst_and_threshold(chip, + message->spi, + bits, &dma_burst, + &dma_thresh)) + dev_warn_ratelimited(&message->spi->dev, + "pump_transfers:" + "DMA burst size reduced to" + "match bits_per_word\n"); + } + + top_ctrl &= ~TOP_DSS_MASK; + top_ctrl |= TOP_DSS(32); + } + } + + message->state = RUNNING_STATE; + + drv_data->dma_mapped = 0; + if (k1x_spi_dma_is_possible(drv_data->len)) + drv_data->dma_mapped = k1x_spi_map_dma_buffers(drv_data); + if (drv_data->dma_mapped) { + /* Ensure we have the correct interrupt handler */ + drv_data->transfer_handler = k1x_spi_dma_transfer; + + k1x_spi_dma_prepare(drv_data, dma_burst); + + /* Clear status and start DMA engine */ + fifo_ctrl |= chip->fifo_ctrl | dma_thresh | drv_data->dma_fifo_ctrl; + top_ctrl |= chip->top_ctrl | drv_data->dma_top_ctrl; + k1x_spi_write(drv_data, STATUS, drv_data->clear_sr); + k1x_spi_dma_start(drv_data); + int_en = k1x_spi_read(drv_data, INT_EN) | drv_data->dma_cr; + } else { + /* Ensure we have the correct interrupt handler */ + drv_data->transfer_handler = interrupt_transfer; + + fifo_ctrl = fifo_ctrl | chip->fifo_ctrl | chip->threshold; + int_en = k1x_spi_read(drv_data, INT_EN) | drv_data->int_cr; + k1x_spi_write(drv_data, STATUS, drv_data->clear_sr); + } + + k1x_spi_write(drv_data, TO, chip->timeout); + + cs_assert(drv_data); + + /* + * TODO: refine these logic + * k1x_spi_get_ssrc1_change_mask + * if ((k1x_spi_read(drv_data, SSCR0) != cr0) + * cs_assert(drv_data); + * k1x_spi_write(drv_data, SSCR1, cr1); + */ + + set_dvfm_constraint(drv_data); /*disable system to idle while DMA */ + if (drv_data->slave_mode) + top_ctrl |= TOP_SSE | TOP_SCLKDIR | TOP_SFRMDIR; + else + top_ctrl |= TOP_HOLD_FRAME_LOW; + /* + * This part changed the logic + * 1. clear SSE + * 2. write TOP_CTRL and other register + * 3. set SSE in the end of this function + */ + top_ctrl &= ~TOP_SSE; + k1x_spi_write(drv_data, TOP_CTRL, top_ctrl); + k1x_spi_write(drv_data, FIFO_CTRL, fifo_ctrl); + k1x_spi_write(drv_data, INT_EN, int_en); + top_ctrl |= TOP_SSE; +#ifdef CONFIG_K1X_SSP_DEBUG + dev_err(&message->spi->dev, "spi top = 0x%x\n", top_ctrl); + dev_err(&message->spi->dev, "fifo = 0x%x\n", k1x_spi_read(drv_data, FIFO_CTRL)); + dev_err(&message->spi->dev, "int_en = 0x%x\n", k1x_spi_read(drv_data, INT_EN)); + dev_err(&message->spi->dev, "to = 0x%x\n", k1x_spi_read(drv_data, TO)); +#endif + k1x_spi_write(drv_data, TOP_CTRL, top_ctrl); +} + +static int k1x_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_driver_data *drv_data = spi_master_get_devdata(master); + + drv_data->cur_msg = msg; + /* Initial message state*/ + drv_data->cur_msg->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, + transfer_list); + + /* + * prepare to setup the SSP, in pump_transfers, using the per + * chip configuration + */ + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + + if (master->max_speed_hz != drv_data->cur_transfer->speed_hz) { + master->max_speed_hz = drv_data->cur_transfer->speed_hz; + clk_set_rate(drv_data->clk, master->max_speed_hz); + } + + reinit_completion(&drv_data->cur_msg_completion); + /* Mark as busy and launch transfers */ + queue_work(system_wq, &drv_data->pump_transfers); + wait_for_completion(&drv_data->cur_msg_completion); + + return 0; +} + +static int k1x_spi_unprepare_transfer(struct spi_master *master) +{ + struct spi_driver_data *drv_data = spi_master_get_devdata(master); + + /* Disable the SSP now */ + k1x_spi_write(drv_data, TOP_CTRL, + k1x_spi_read(drv_data, TOP_CTRL) & ~(TOP_SSE | TOP_HOLD_FRAME_LOW)); + + return 0; +} + +static int setup_cs(struct spi_device *spi, struct chip_data *chip) +{ + int err = 0; + + if (chip == NULL) + return 0; + return err; +} + +static int setup(struct spi_device *spi) +{ + struct chip_data *chip; + struct spi_driver_data *drv_data = spi_master_get_devdata(spi->master); + uint tx_thres, tx_hi_thres, rx_thres; + + tx_thres = TX_THRESH_DFLT; + tx_hi_thres = 0; + rx_thres = RX_THRESH_DFLT; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + if (!chip) { + chip = devm_kzalloc(&spi->master->dev, sizeof(struct chip_data), + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->gpio_cs = -1; + chip->enable_dma = 0; + chip->timeout = + drv_data->slave_mode ? TIMOUT_DFLT_SLAVE : TIMOUT_DFLT; + } + + chip->top_ctrl = 0; + chip->fifo_ctrl = 0; + + chip->enable_dma = drv_data->master_info->enable_dma; + if (drv_data->slave_mode) + chip->dma_burst_size = 32; + + if (chip->enable_dma) { + /* set up legal burst and threshold for dma */ + if (k1x_spi_set_dma_burst_and_threshold(chip, spi, + spi->bits_per_word, + &chip->dma_burst_size, + &chip->dma_threshold)) { + dev_warn(&spi->dev, + "in setup: DMA burst size reduced to match bits_per_word\n"); + } + } + chip->threshold = (FIFO_RxTresh(rx_thres) & FIFO_RFT) | + (FIFO_TxTresh(tx_thres) & FIFO_TFT); + + chip->top_ctrl &= ~(TOP_SPO | TOP_SPH); + chip->top_ctrl |= (((spi->mode & SPI_CPHA) != 0) ? TOP_SPH : 0) + | (((spi->mode & SPI_CPOL) != 0) ? TOP_SPO : 0); + + if (spi->mode & SPI_LOOP) + chip->top_ctrl |= TOP_LBM; + + /* Enable rx fifo auto full control */ + if (drv_data->ssp_enhancement) + chip->fifo_ctrl |= FIFO_RXFIFO_AUTO_FULL_CTRL; + + if (spi->bits_per_word <= 8) { + chip->n_bytes = 1; + chip->read = u8_reader; + chip->write = u8_writer; + } else if (spi->bits_per_word <= 16) { + chip->n_bytes = 2; + chip->read = u16_reader; + chip->write = u16_writer; + } else if (spi->bits_per_word <= 32) { + chip->n_bytes = 4; + chip->read = u32_reader; + chip->write = u32_writer; + } + + if (spi->master->max_speed_hz != spi->max_speed_hz) { + spi->master->max_speed_hz = spi->max_speed_hz; + clk_set_rate(drv_data->clk, spi->master->max_speed_hz); + } + + spi_set_ctldata(spi, chip); + + return setup_cs(spi, chip); +} + +static void cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + if (!chip) + return; + + if (gpio_is_valid(chip->gpio_cs)) + gpio_free(chip->gpio_cs); + + devm_kfree(&spi->dev, chip); +} + +static const struct of_device_id k1x_spi_dt_ids[] = { + { .compatible = "spacemit,k1x-spi", .data = (void *) K1X_SSP }, + {} +}; +MODULE_DEVICE_TABLE(of, k1x_spi_dt_ids); + +static int k1x_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k1x_spi_master *platform_info; + struct spi_master *master = NULL; + struct spi_driver_data *drv_data = NULL; + struct device_node *np = dev->of_node; + const struct of_device_id *id = + of_match_device(of_match_ptr(k1x_spi_dt_ids), dev); + struct resource *iores; + u32 bus_num; +#if 0 + const __be32 *prop; + unsigned int proplen; +#endif + int status; + u32 tmp; + + platform_info = dev_get_platdata(dev); + if (!platform_info) { + platform_info = devm_kzalloc(dev, sizeof(*platform_info), + GFP_KERNEL); + if (!platform_info) + return -ENOMEM; + platform_info->num_chipselect = 1; + /* TODO: NO DMA on FPGA yet */ + if (of_get_property(np, "k1x,ssp-disable-dma", NULL)) + platform_info->enable_dma = 0; + else + platform_info->enable_dma = 1; + } + + master = spi_alloc_master(dev, sizeof(struct spi_driver_data)); + if (!master) { + dev_err(&pdev->dev, "cannot alloc spi_master\n"); + return -ENOMEM; + } + drv_data = spi_master_get_devdata(master); + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (iores == NULL) { + dev_err(dev, "no memory resource defined\n"); + status = -ENODEV; + goto out_error_master_alloc; + } + + drv_data->ioaddr = devm_ioremap_resource(dev, iores); + if (drv_data->ioaddr == NULL) { + dev_err(dev, "failed to ioremap() registers\n"); + status = -ENODEV; + goto out_error_master_alloc; + } + + drv_data->irq = platform_get_irq(pdev, 0); + if (drv_data->irq < 0) { + dev_err(dev, "no IRQ resource defined\n"); + status = -ENODEV; + goto out_error_master_alloc; + } + + /* Receive FIFO auto full ctrl enable */ + if (of_get_property(np, "k1x,ssp-enhancement", NULL)) + drv_data->ssp_enhancement = 1; + + if (of_get_property(np, "k1x,ssp-slave-mode", NULL)) { + drv_data->slave_mode = 1; + dev_warn(&pdev->dev, "slave mode\n"); + timer_setup(&drv_data->slave_rx_timer, + slave_rx_timer_expired, 0); + } + +#if 0 + prop = of_get_property(dev->of_node, "k1x,ssp-lpm-qos", &proplen); + if (!prop) { + dev_err(&pdev->dev, "lpm-qos for spi is not defined!\n"); + status = -EINVAL; + goto out_error_master_alloc; + } else + drv_data->qos_idle_value = be32_to_cpup(prop); +#endif + + init_dvfm_constraint(drv_data); + + master->dev.of_node = dev->of_node; + drv_data->ssp_type = (uintptr_t) id->data; + if (!of_property_read_u32(np, "k1x,ssp-id", &bus_num)) + master->bus_num = bus_num; + drv_data->ssdr_physical = iores->start + DATAR; + + drv_data->clk = devm_clk_get(dev, NULL); + if (IS_ERR_OR_NULL(drv_data->clk)) { + dev_err(&pdev->dev, "cannot get clk\n"); + status = -ENODEV; + goto out_error_clk_check; + } + + drv_data->reset = devm_reset_control_get_optional(dev, NULL); + if (IS_ERR_OR_NULL(drv_data->reset)) { + dev_err(&pdev->dev, "Failed to get spi's reset\n"); + goto out_error_clk_check; + } + + drv_data->master = master; + drv_data->master_info = platform_info; + drv_data->pdev = pdev; + + master->dev.parent = &pdev->dev; + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + + master->dma_alignment = DMA_ALIGNMENT; + master->cleanup = cleanup; + master->setup = setup; + master->transfer_one_message = k1x_spi_transfer_one_message; + master->unprepare_transfer_hardware = k1x_spi_unprepare_transfer; + master->auto_runtime_pm = true; + + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + drv_data->int_cr = INT_EN_TIE | INT_EN_RIE | INT_EN_TINTE; /* INT_EN */ + drv_data->dma_cr = (drv_data->slave_mode) ? INT_EN_TINTE : 0; + drv_data->clear_sr = STATUS_ROR | STATUS_TINT; + drv_data->mask_sr = STATUS_TINT | STATUS_RFS | STATUS_TFS | STATUS_ROR; + drv_data->dma_top_ctrl = DEFAULT_DMA_TOP_CTRL; + drv_data->dma_fifo_ctrl = DEFAULT_DMA_FIFO_CTRL; + + status = devm_request_irq(&pdev->dev, drv_data->irq, ssp_int, IRQF_SHARED, dev_name(dev), + drv_data); + if (status < 0) { + dev_err(&pdev->dev, "cannot get IRQ %d\n", drv_data->irq); + goto out_error_master_alloc; + } + + /* Setup DMA if requested */ + if (platform_info->enable_dma) { + status = k1x_spi_dma_setup(drv_data); + if (status) { + dev_dbg(dev, "no DMA channels available, using PIO\n"); + platform_info->enable_dma = false; + } + } + + status = of_property_read_u32(np, "k1x,ssp-clock-rate", &master->max_speed_hz); + if (status < 0) { + dev_err(&pdev->dev, "cannot get clock-rate from DT file\n"); + goto out_error_master_alloc; + } + + clk_set_rate(drv_data->clk, master->max_speed_hz); + master->max_speed_hz = clk_get_rate(drv_data->clk); + clk_prepare_enable(drv_data->clk); + reset_control_deassert(drv_data->reset); + + /* Load default SSP configuration */ + k1x_spi_write(drv_data, TOP_CTRL, 0); + k1x_spi_write(drv_data, FIFO_CTRL, 0); + tmp = FIFO_RxTresh(RX_THRESH_DFLT) | + FIFO_TxTresh(TX_THRESH_DFLT); + k1x_spi_write(drv_data, FIFO_CTRL, tmp); + tmp = TOP_FRF_Motorola | TOP_DSS(8); + k1x_spi_write(drv_data, TOP_CTRL, tmp); + k1x_spi_write(drv_data, TO, 0); + + k1x_spi_write(drv_data, PSP_CTRL, 0); + + master->num_chipselect = platform_info->num_chipselect; + + INIT_WORK(&drv_data->pump_transfers, pump_transfers); + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + init_completion(&drv_data->cur_msg_completion); + + /* Register with the SPI framework */ + platform_set_drvdata(pdev, drv_data); + status = devm_spi_register_master(&pdev->dev, master); + if (status != 0) { + dev_err(&pdev->dev, "problem registering spi master\n"); + goto out_error_clock_enabled; + } + + return status; + +out_error_clock_enabled: + reset_control_assert(drv_data->reset); + clk_disable_unprepare(drv_data->clk); + k1x_spi_dma_release(drv_data); + free_irq(drv_data->irq, drv_data); +out_error_clk_check: + deinit_dvfm_constraint(drv_data); +out_error_master_alloc: + spi_master_put(master); + return status; +} + +static int k1x_spi_remove(struct platform_device *pdev) +{ + struct spi_driver_data *drv_data = platform_get_drvdata(pdev); + + if (!drv_data) + return 0; + + pm_runtime_get_sync(&pdev->dev); + + /* Disable the SSP at the peripheral and SOC level */ + k1x_spi_write(drv_data, TOP_CTRL, 0); + k1x_spi_write(drv_data, FIFO_CTRL, 0); /* whether need this line? */ + + reset_control_assert(drv_data->reset); + clk_disable_unprepare(drv_data->clk); + + /* Release DMA */ + if (drv_data->master_info->enable_dma) + k1x_spi_dma_release(drv_data); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + /* Release IRQ */ + free_irq(drv_data->irq, drv_data); + + deinit_dvfm_constraint(drv_data); + return 0; +} + +static void k1x_spi_shutdown(struct platform_device *pdev) +{ + int status = 0; + + if ((status = k1x_spi_remove(pdev)) != 0) + dev_err(&pdev->dev, "shutdown failed with %d\n", status); +} + +#ifdef CONFIG_PM_SLEEP +static int k1x_spi_suspend(struct device *dev) +{ + struct spi_driver_data *drv_data = dev_get_drvdata(dev); + int status = 0; + + pm_runtime_get_sync(dev); + status = spi_master_suspend(drv_data->master); + if (status != 0) + return status; + k1x_spi_write(drv_data, TOP_CTRL, 0); + k1x_spi_write(drv_data, FIFO_CTRL, 0); /* whether need this line? */ + + status = pm_runtime_force_suspend(dev); + + return status; +} + +static int k1x_spi_resume(struct device *dev) +{ + struct spi_driver_data *drv_data = dev_get_drvdata(dev); + int status = 0; + + /* Enable the SSP clock */ + status = pm_runtime_force_resume(dev); + if (status) { + dev_err(dev, "failed to resume pm_runtime (%d)\n", status); + return status; + } + + /* Start the queue running */ + status = spi_master_resume(drv_data->master); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + if (status != 0) { + dev_err(dev, "problem starting queue (%d)\n", status); + return status; + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +/** static int k1x_spi_runtime_suspend(struct device *dev) + * { + * struct spi_driver_data *drv_data = dev_get_drvdata(dev); + * + * reset_control_assert(drv_data->reset); + * clk_disable_unprepare(drv_data->clk); + * + * return 0; + *} + */ + +/** + * static int k1x_spi_runtime_resume(struct device *dev) + * { + * struct spi_driver_data *drv_data = dev_get_drvdata(dev); + * + * clk_prepare_enable(drv_data->clk); + * reset_control_deassert(drv_data->reset); + * return 0; + *} + */ +#endif + +static const struct dev_pm_ops k1x_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(k1x_spi_suspend, k1x_spi_resume) + /** + * SET_RUNTIME_PM_OPS(k1x_spi_runtime_suspend, + * k1x_spi_runtime_resume, NULL) + */ +}; + +static struct platform_driver driver = { + .driver = { + .name = "k1x-spi", + .pm = &k1x_spi_pm_ops, + .of_match_table = k1x_spi_dt_ids, + }, + .probe = k1x_spi_probe, + .remove = k1x_spi_remove, + .shutdown = k1x_spi_shutdown, +}; + +static int __init k1x_spi_init(void) +{ + return platform_driver_register(&driver); +} +module_init(k1x_spi_init); + +static void __exit k1x_spi_exit(void) +{ + platform_driver_unregister(&driver); +} +module_exit(k1x_spi_exit); + +MODULE_AUTHOR("Spacemit"); +MODULE_DESCRIPTION("Spacemit k1x spi controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-k1x.h b/drivers/spi/spi-k1x.h new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/spi/spi-k1x.h @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for Spacemit k1x spi controller + * + * Copyright (c) 2023, spacemit Corporation. + * + */ + +#ifndef _SPI_K1X_H +#define _SPI_K1X_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Spacemit k1x SPI Registers */ +#define TOP_CTRL 0x00 /* SSP Top Control Register */ +#define FIFO_CTRL 0x04 /* SSP FIFO Control Register */ +#define INT_EN 0x08 /* SSP Interrupt Enable Register */ +#define TO 0x0C /* SSP Time Out Register */ +#define DATAR 0x10 /* SSP Data Register */ +#define STATUS 0x14 /* SSP Stauts Register */ +#define PSP_CTRL 0x18 /* SSP Programmable Serial Protocal Control Register */ +#define NET_WORK_CTRL 0x1C /* SSP NET Work Control Register */ +#define NET_WORK_STATUS 0x20 /* SSP Net Work Status Register */ +#define RWOT_CTRL 0x24 /* SSP RWOT Control Register */ +#define RWOT_CCM 0x28 /* SSP RWOT Counter Cycles Match Register */ +#define RWOT_CVWRn 0x2C /* SSP RWOT Counter Value Write for Read Request Register */ + +/* 0x00 TOP_CTRL */ +#define TOP_TTELP (1 << 18) +#define TOP_TTE (1 << 17) +#define TOP_SCFR (1 << 16) +#define TOP_IFS (1 << 15) +#define TOP_HOLD_FRAME_LOW (1 << 14) +#define TOP_TRAIL (1 << 13) +#define TOP_LBM (1 << 12) +#define TOP_SPH (1 << 11) +#define TOP_SPO (1 << 10) +#define TOP_DSS(x) ((x - 1) << 5) +#define TOP_DSS_MASK (0x1F << 5) +#define TOP_SFRMDIR (1 << 4) +#define TOP_SCLKDIR (1 << 3) +#define TOP_FRF_MASK (0x3 << 1) +#define TOP_FRF_Motorola (0x0 << 1) /* Motorola's Serial Peripheral Interface (SPI) */ +#define TOP_FRF_TI (0x1 << 1) /* Texas Instruments' Synchronous Serial Protocol (SSP) */ +#define TOP_FRF_National (0x2 << 1) /* National Microwire */ +#define TOP_FRF_PSP (0x3 << 1) /* Programmable Serial Protocol(PSP) */ +#define TOP_SSE (1 << 0) + +/* 0x04 FIFO_CTRL */ +#define FIFO_STRF (1 << 19) +#define FIFO_EFWR (1 << 18) +#define FIFO_RXFIFO_AUTO_FULL_CTRL (1 << 17) +#define FIFO_FPCKE (1 << 16) +#define FIFO_TXFIFO_WR_ENDIAN_MASK (0x3 << 14) +#define FIFO_RXFIFO_RD_ENDIAN_MASK (0x3 << 12) +#define FIFO_WR_ENDIAN_16BITS (1 << 14) /* Swap first 16 bits and last 16 bits */ +#define FIFO_WR_ENDIAN_8BITS (2 << 14) /* Swap all 4 bytes */ +#define FIFO_RD_ENDIAN_16BITS (1 << 12) /* Swap first 16 bits and last 16 bits */ +#define FIFO_RD_ENDIAN_8BITS (2 << 12) /* Swap all 4 bytes */ +#define FIFO_RSRE (1 << 11) +#define FIFO_TSRE (1 << 10) + +/* 0x08 INT_EN */ +#define INT_EN_EBCEI (1 << 6) +#define INT_EN_TIM (1 << 5) +#define INT_EN_RIM (1 << 4) +#define INT_EN_TIE (1 << 3) +#define INT_EN_RIE (1 << 2) +#define INT_EN_TINTE (1 << 1) +#define INT_EN_PINTE (1 << 0) + +/* 0x0C TO */ +#define TIMEOUT(x) ((x) << 0) + +/* 0x10 DATAR */ +#define DATA(x) ((x) << 0) + +/* 0x14 STATUS */ +#define STATUS_OSS (1 << 23) +#define STATUS_TX_OSS (1 << 22) +#define STATUS_BCE (1 << 21) +#define STATUS_ROR (1 << 20) +#define STATUS_RNE (1 << 14) +#define STATUS_RFS (1 << 13) +#define STATUS_TUR (1 << 12) +#define STATUS_TNF (1 << 6) +#define STATUS_TFS (1 << 5) +#define STATUS_EOC (1 << 4) +#define STATUS_TINT (1 << 3) +#define STATUS_PINT (1 << 2) +#define STATUS_CSS (1 << 1) +#define STATUS_BSY (1 << 0) + +/* 0x18 PSP_CTRL */ +#define PSP_EDMYSTOP(x) ((x) << 27) +#define PSP_EMYSTOP(x) ((x) << 25) +#define PSP_EDMYSTRT(x) ((x) << 23) +#define PSP_DMYSTRT(x) ((x) << 21) +#define PSP_STRTDLY(x) ((x) << 18) +#define PSP_SFRMWDTH(x) ((x) << 12) +#define PSP_SFRMDLY(x) ((x) << 5) +#define PSP_SFRMP (1 << 4) +#define PSP_FSRT (1 << 3) +#define PSP_ETDS (1 << 2) +#define PSP_SCMODE(x) ((x) << 0) + +/* 0x1C NET_WORK_CTRL */ +#define RTSA(x) ((x) << 12) +#define RTSA_MASK (0xFF << 12) +#define TTSA(x) ((x) << 4) +#define TTSA_MASK (0xFF << 4) +#define NET_FRDC(x) ((x) << 1) +#define NET_WORK_MODE (1 << 0) + +/* 0x20 NET_WORK_STATUS */ +#define NET_SATUS_NMBSY (1 << 3) +#define NET_STATUS_TSS(x) ((x) << 0) + +/* 0x24 RWOT_CTRL */ +#define RWOT_MASK_RWOT_LAST_SAMPLE (1 << 4) +#define RWOT_CLR_RWOT_CYCLE (1 << 3) +#define RWOT_SET_RWOT_CYCLE (1 << 2) +#define RWOT_CYCLE_RWOT_EN (1 << 1) +#define RWOT_RWOT (1 << 0) + +enum k1x_ssp_type { + SSP_UNDEFINED = 0, + K1X_SSP, +}; + +struct spi_driver_data { + /* Driver model hookup */ + struct platform_device *pdev; + + /* SSP Info */ + struct ssp_device *ssp; + + /* SPI framework hookup */ + enum k1x_ssp_type ssp_type; + struct spi_master *master; + + /* k1x hookup */ + struct k1x_spi_master *master_info; + + /* SSP register addresses */ + void __iomem *ioaddr; + u32 ssdr_physical; + + /* SSP masks*/ + u32 dma_fifo_ctrl; + u32 dma_top_ctrl; + u32 int_cr; + u32 dma_cr; + u32 clear_sr; + u32 mask_sr; + + /* Message Transfer pump */ + struct work_struct pump_transfers; + + /* DMA engine support */ + struct dma_chan *rx_chan; + struct dma_chan *tx_chan; + struct sg_table rx_sgt; + struct sg_table tx_sgt; + int rx_nents; + int tx_nents; + void *dummy; + atomic_t dma_running; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + struct completion cur_msg_completion; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + int dma_mapped; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + size_t rx_map_len; + size_t tx_map_len; + u8 n_bytes; + int (*write)(struct spi_driver_data *drv_data); + int (*read)(struct spi_driver_data *drv_data); + irqreturn_t (*transfer_handler)(struct spi_driver_data *drv_data); + void (*cs_control)(u32 command); + struct freq_qos_request qos_idle; + int qos_idle_value; + struct clk *clk; + struct reset_control *reset; + int irq; + /* Support RX FIFO auto full control and endian swap */ + unsigned int ssp_enhancement; + unsigned char slave_mode; + struct timer_list slave_rx_timer; +}; + +struct chip_data { + u32 top_ctrl; + u32 fifo_ctrl; + u32 timeout; + u8 n_bytes; + u32 dma_burst_size; + u32 threshold; + u32 dma_threshold; + u8 enable_dma; + union { + int gpio_cs; + unsigned int frm; + }; + int gpio_cs_inverted; + int (*write)(struct spi_driver_data *drv_data); + int (*read)(struct spi_driver_data *drv_data); + void (*cs_control)(u32 command); +}; + +static inline u32 k1x_spi_read(const struct spi_driver_data *drv_data, + unsigned reg) +{ + return __raw_readl(drv_data->ioaddr + reg); +} + +static inline void k1x_spi_write(const struct spi_driver_data *drv_data, + unsigned reg, u32 val) +{ + __raw_writel(val, drv_data->ioaddr + reg); +} + +#define START_STATE ((void *)0) +#define RUNNING_STATE ((void *)1) +#define DONE_STATE ((void *)2) +#define ERROR_STATE ((void *)-1) + +#define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT) +#define DMA_ALIGNMENT 64 + +extern int k1x_spi_flush(struct spi_driver_data *drv_data); +extern void *k1x_spi_next_transfer(struct spi_driver_data *drv_data); + +/* + * Select the right DMA implementation. + */ +#define MAX_DMA_LEN SZ_512K +#define DEFAULT_DMA_FIFO_CTRL (FIFO_TSRE | FIFO_RSRE) +#define DEFAULT_DMA_TOP_CTRL (TOP_TRAIL) + +extern bool k1x_spi_dma_is_possible(size_t len); +extern int k1x_spi_map_dma_buffers(struct spi_driver_data *drv_data); +extern irqreturn_t k1x_spi_dma_transfer(struct spi_driver_data *drv_data); +extern void k1x_spi_slave_sw_timeout_callback(struct spi_driver_data *drv_data); +extern int k1x_spi_dma_prepare(struct spi_driver_data *drv_data, u32 dma_burst); +extern void k1x_spi_dma_start(struct spi_driver_data *drv_data); +extern int k1x_spi_dma_setup(struct spi_driver_data *drv_data); +extern void k1x_spi_dma_release(struct spi_driver_data *drv_data); +extern int k1x_spi_set_dma_burst_and_threshold(struct chip_data *chip, + struct spi_device *spi, + u8 bits_per_word, + u32 *burst_code, + u32 *threshold); + +#define RX_THRESH_DFLT 9 +#define TX_THRESH_DFLT 8 +/* 0x14 */ +#define STATUS_TFL_MASK (0x1f << 7) /* Transmit FIFO Level mask */ +#define STATUS_RFL_MASK (0x1f << 15) /* Receive FIFO Level mask */ +/* 0x4 */ +#define FIFO_TFT (0x0000001F) /* Transmit FIFO Threshold (mask) */ +#define FIFO_TxTresh(x) (((x) - 1) << 0) /* level [1..32] */ +#define FIFO_RFT (0x000003E0) /* Receive FIFO Threshold (mask) */ +#define FIFO_RxTresh(x) (((x) - 1) << 5) /* level [1..32] */ + +struct ssp_device { + struct platform_device *pdev; + struct list_head node; + + struct clk *clk; + void __iomem *mmio_base; + unsigned long phys_base; + + const char *label; + int port_id; + int type; + int use_count; + int irq; + int drcmr_rx; + int drcmr_tx; + + struct device_node *of_node; +}; + +/** + * k1x_ssp_write_reg - Write to a SSP register + * + * @dev: SSP device to access + * @reg: Register to write to + * @val: Value to be written. + */ +static inline void k1x_ssp_write_reg(struct ssp_device *dev, u32 reg, u32 val) +{ + __raw_writel(val, dev->mmio_base + reg); +} + +/** + * k1x_ssp_read_reg - Read from a SSP register + * + * @dev: SSP device to access + * @reg: Register to read from + */ +static inline u32 k1x_ssp_read_reg(struct ssp_device *dev, u32 reg) +{ + return __raw_readl(dev->mmio_base + reg); +} + +static inline void k1x_ssp_free(struct ssp_device *ssp) {} +#define K1X_CS_ASSERT (0x01) +#define K1X_CS_DEASSERT (0x02) + +struct dma_chan; + +/* device.platform_data for SSP controller devices */ +struct k1x_spi_master { + u16 num_chipselect; + u8 enable_dma; + + /* DMA engine specific config */ + bool (*dma_filter)(struct dma_chan *chan, void *param); + void *tx_param; + void *rx_param; + + /* For sound ssp controller */ + struct ssp_device ssp; +}; + +/* spi_board_info.controller_data for SPI slave devices, + * copied to spi_device.platform_data ... mostly for dma tuning + */ +struct k1x_spi_chip { + u8 tx_threshold; + u8 tx_hi_threshold; + u8 rx_threshold; + u8 dma_burst_size; + u32 timeout; + u8 enable_loopback; + int gpio_cs; + void (*cs_control)(u32 command); +}; + +#endif /* _SPI_K1X_H */ -- Armbian