diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 0fba9016138b9..3fb9e0c0f03d7 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -3316,6 +3316,8 @@ Load: dtoverlay=jedec-spi-nor,= Params: spi- Enable flash device on SPI, CS# fastr Add fast read capability to the flash device speed Maximum SPI frequency (Hz) + quad Enable quad (4-wire) I/O mode, which also + uses GPIOs 0 and 1 flash-spi- Same as spi- (deprecated) flash-fastr-spi- Same as spi->m>,fastr (deprecated) diff --git a/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts b/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts index fb6d4bc91bf3c..4b3bf153adf9c 100644 --- a/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts +++ b/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts @@ -4,6 +4,7 @@ // flash-spi- - Enables flash device on SPI, CS#. // flash-fastr-spi- - Enables flash device with fast read capability on SPI, CS#. // speed - Set the SPI clock speed in Hz +// quad - Enable quad (4-wire) I/O mode // // If devices are present on SPI1 or SPI2, those interfaces must be enabled with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays. // @@ -89,6 +90,13 @@ }; }; + fragment@9 { + target = <&spi0_pins>; + __dormant__ { + pins = "gpio9", "gpio10", "gpio11", "gpio0", "gpio1"; + }; + }; + payload: fragment@100 { target = <&spi0>; __overlay__ { @@ -100,6 +108,9 @@ compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <500000>; + /* The defaults. The quad parameter changes them. */ + spi-rx-bus-width = <1>; + spi-tx-bus-width = <1>; }; }; }; @@ -131,6 +142,9 @@ flash-fastr-spi2-2 = <0>,"+7+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2"; fastr = <0>,"+8"; speed = <&spi_nor>, "spi-max-frequency:0"; + quad = <0>,"+9", + <&spi_nor>,"spi-rx-bus-width:0=4", + <&spi_nor>,"spi-tx-bus-width:0=4"; }; }; diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 18d9a00aa22eb..6c473006f948f 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -99,6 +99,7 @@ static const struct flash_info issi_nor_parts[] = { .id = SNOR_ID(0x9d, 0x60, 0x18), .name = "is25lp128", .size = SZ_16M, + .flags = SPI_NOR_QUAD_PP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ, }, { .id = SNOR_ID(0x9d, 0x60, 0x19), diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c index 4a5be813efa75..90caf790ebe75 100644 --- a/drivers/spi/spi-dw-bt1.c +++ b/drivers/spi/spi-dw-bt1.c @@ -135,7 +135,7 @@ static ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc, dw_spi_enable_chip(dws, 0); - dw_spi_update_config(dws, mem->spi, &cfg); + dw_spi_update_config(dws, mem->spi, &cfg, NULL); dw_spi_umask_intr(dws, DW_SPI_INT_RXFI); diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index bb6989b4b6185..90692396b12d1 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -375,7 +375,7 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi) } void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, - struct dw_spi_cfg *cfg) + struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *ecfg) { struct dw_spi_chip_data *chip = spi_get_ctldata(spi); u32 cr0 = chip->cr0; @@ -385,15 +385,27 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, /* 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)) + if (dw_spi_ip_is(dws, PSSI)) { /* CTRLR0[ 9:8] Transfer Mode */ cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_TMOD_MASK, cfg->tmode); - else + /* CTRLR0[22:21] Enhanced SPI frame format (quad) */ + if (dws->caps & DW_SPI_CAP_QUAD) + cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_ENH_FRF_MASK, cfg->enh_frf); + } else { /* CTRLR0[11:10] Transfer Mode */ cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_TMOD_MASK, cfg->tmode); + } dw_writel(dws, DW_SPI_CTRLR0, cr0); + if (ecfg) { + cr0 = FIELD_PREP(DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK, ecfg->trans_type) | + FIELD_PREP(DW_SPI_ENH_CTRLR0_ADDR_L_MASK, ecfg->addr_l) | + FIELD_PREP(DW_SPI_ENH_CTRLR0_INST_L_MASK, ecfg->inst_l) | + FIELD_PREP(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK, ecfg->wait_l); + dw_writel(dws, DW_SPI_ENH_CTRLR0, cr0); + } + if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD || cfg->tmode == DW_SPI_CTRLR0_TMOD_RO) dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); @@ -415,11 +427,144 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, } EXPORT_SYMBOL_NS_GPL(dw_spi_update_config, "SPI_DW_CORE"); +/* + * The controller transmits only while the Tx FIFO is non-empty, so a frame has + * to be initiated by a write to it. A standard receive-only frame needs a + * single dummy word. An enhanced frame is initiated by its control words. For + * a write the data must follow at once, since the frame ends as soon as the + * Tx FIFO drains. + */ +void dw_spi_start_frame(struct dw_spi *dws) +{ + if (dws->enh.ctrl_len) { + dw_write_io_reg(dws, DW_SPI_DR, dws->enh.inst); + if (dws->enh.ctrl_len > 1) + dw_write_io_reg(dws, DW_SPI_DR, dws->enh.addr); + + /* The control words are consumed and must not be repeated */ + memset(&dws->enh, 0, sizeof(dws->enh)); + + if (dws->tx_len) + dw_writer(dws); + } else if (!dws->tx_len) { + dw_writel(dws, DW_SPI_DR, 0); + } +} + +/* True if the multi-lane data of an enhanced frame is still ahead */ +static bool dw_spi_enh_data_pending(struct spi_controller *host, + struct spi_transfer *xfer) +{ + struct spi_transfer *next = xfer; + + list_for_each_entry_continue(next, &host->cur_msg->transfers, transfer_list) + if (next->tx_nbits > 1 || next->rx_nbits > 1) + return true; + + return false; +} + +/* + * Collect one transfer into the enhanced frame in dws->enh to be written to + * the FIFO later. The instruction comes first then the address. + */ +static int dw_spi_collect_enh_xfer(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + unsigned int cycles, i; + + if (!xfer->tx_buf) + return -EINVAL; + + if (xfer->dummy_data) { + /* Dummy bytes set WAIT_CYCLES, which counts sclk periods */ + cycles = xfer->len * BITS_PER_BYTE / xfer->tx_nbits; + if (cycles > FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK)) + return -EINVAL; + + dws->enh.cfg.wait_l = cycles; + } else if (!dws->enh.ctrl_len) { + /* The instruction comes first, INST_L is a width */ + if (xfer->len == 1) + dws->enh.cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_8BIT; + else if (xfer->len == 2) + dws->enh.cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_16BIT; + else + return -EINVAL; + + for (i = 0; i < xfer->len; i++) + dws->enh.inst = dws->enh.inst << BITS_PER_BYTE | + ((const u8 *)xfer->tx_buf)[i]; + + dws->enh.ctrl_len = 1; + } else if (dws->enh.ctrl_len == 1) { + /* Then the address, one FIFO entry, ADDR_L counts nibbles */ + if (xfer->len > sizeof(dws->enh.addr)) + return -EINVAL; + + dws->enh.cfg.addr_l = xfer->len * 2; + if (xfer->tx_nbits > 1) + dws->enh.cfg.trans_type = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1; + + for (i = 0; i < xfer->len; i++) + dws->enh.addr = dws->enh.addr << BITS_PER_BYTE | + ((const u8 *)xfer->tx_buf)[i]; + + dws->enh.ctrl_len = 2; + } else { + /* Nothing else can precede the data */ + return -EINVAL; + } + + return 0; +} + +/* + * Check the collected frame against the transfer and select its format. Only + * spi-mem ops are filtered earlier by dw_spi_supports_enh_mem_op(). + */ +static int dw_spi_check_enh_frame(struct dw_spi *dws, struct dw_spi_cfg *cfg, + int buswidth) +{ + /* Quad is the only frame format the driver implements */ + if (buswidth != 4 || !(dws->caps & DW_SPI_CAP_QUAD)) + return -EOPNOTSUPP; + + /* An enhanced frame always begins with an instruction */ + if (!dws->enh.ctrl_len) + return -EINVAL; + + /* The control words are byte sized, so DFS has to be 8 */ + if (dws->n_bytes != 1) + return -EINVAL; + + /* There is no full duplex enhanced frame */ + if (cfg->tmode != DW_SPI_CTRLR0_TMOD_RO && + cfg->tmode != DW_SPI_CTRLR0_TMOD_TO) + return -EINVAL; + + /* A write ends when the Tx FIFO drains, so all of it has to fit */ + if (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO && + dws->tx_len + dws->enh.ctrl_len > dws->fifo_len) + return -EINVAL; + + cfg->enh_frf = DW_SPI_CTRLR0_ENH_FRF_QUAD; + + return 0; +} + static void dw_spi_irq_setup(struct dw_spi *dws) { u16 level; u8 imask; + /* + * An enhanced write must be loaded before the interrupts are unmasked, + * or TXEI would feed its data ahead of the control words. + */ + if (dws->tx_len && dws->enh.ctrl_len) + dw_spi_start_frame(dws); + /* * Originally Tx and Rx data lengths match. Rx FIFO Threshold level * will be adjusted at the final stage of the IRQ-based SPI transfer @@ -427,7 +572,7 @@ static void dw_spi_irq_setup(struct dw_spi *dws) */ level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len ? dws->tx_len : dws->rx_len); dw_writel(dws, DW_SPI_TXFTLR, level); - dw_writel(dws, DW_SPI_RXFTLR, level - 1); + dw_writel(dws, DW_SPI_RXFTLR, level ? level - 1 : 0); dws->transfer_handler = dw_spi_transfer_handler; @@ -435,8 +580,9 @@ static void dw_spi_irq_setup(struct dw_spi *dws) if (dws->rx_len) imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; dw_spi_umask_intr(dws, imask); - if (!dws->tx_len) - dw_writel(dws, DW_SPI_DR, 0); + + if (!dws->tx_len && dws->rx_len) + dw_spi_start_frame(dws); } /* @@ -459,8 +605,7 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, delay.unit = SPI_DELAY_UNIT_SCK; nbits = dws->n_bytes * BITS_PER_BYTE; - if (!dws->tx_len) - dw_writel(dws, DW_SPI_DR, 0); + dw_spi_start_frame(dws); do { if (dws->tx_len) @@ -493,6 +638,14 @@ static int dw_spi_transfer_one(struct spi_controller *host, int buswidth; int ret; + /* Start the message with no frame collected */ + if (list_is_first(&transfer->transfer_list, &host->cur_msg->transfers)) + memset(&dws->enh, 0, sizeof(dws->enh)); + + /* Not transmitted here, the data transfer issues the whole frame */ + if (dw_spi_enh_data_pending(host, transfer)) + return dw_spi_collect_enh_xfer(dws, transfer); + dws->dma_mapped = 0; dws->n_bytes = spi_bpw_to_bytes(transfer->bits_per_word); dws->tx = (void *)transfer->tx_buf; @@ -508,15 +661,22 @@ static int dw_spi_transfer_one(struct spi_controller *host, cfg.tmode = DW_SPI_CTRLR0_TMOD_RO; cfg.ndf = dws->rx_len; } - buswidth = transfer->rx_buf ? transfer->rx_nbits : - (transfer->tx_buf ? transfer->tx_nbits : 1); + + /* A multi-lane transfer here is the data of an enhanced frame */ + buswidth = transfer->rx_buf ? transfer->rx_nbits : transfer->tx_nbits; + if (buswidth > 1) { + ret = dw_spi_check_enh_frame(dws, &cfg, buswidth); + if (ret) + return ret; + } /* Ensure the data above is visible for all CPUs */ smp_mb(); dw_spi_enable_chip(dws, 0); - dw_spi_update_config(dws, spi, &cfg); + dw_spi_update_config(dws, spi, &cfg, + buswidth > 1 ? &dws->enh.cfg : NULL); transfer->effective_speed_hz = dws->current_freq; @@ -552,13 +712,20 @@ static void dw_spi_handle_err(struct spi_controller *host, if (dws->dma_mapped) dws->dma_ops->dma_stop(dws); + memset(&dws->enh, 0, sizeof(dws->enh)); dw_spi_reset_chip(dws); } static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) { + struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller); + if (op->data.dir == SPI_MEM_DATA_IN) op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK + 1); + else if (op->data.buswidth > 1) + /* An enhanced write is limited to what the Tx FIFO can hold */ + op->data.nbytes = min(op->data.nbytes, + dws->fifo_len - DW_SPI_ENH_CTRL_ENTRIES); return 0; } @@ -573,6 +740,29 @@ static bool dw_spi_supports_mem_op(struct spi_mem *mem, return spi_mem_default_supports_op(mem, op); } +static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller); + + /* The instruction is always sent single-lane */ + if (op->cmd.buswidth > 1) + return false; + + /* Quad is the only multi-lane data width implemented */ + if (op->data.buswidth > 1 && op->data.buswidth != 4) + return false; + if (op->data.buswidth == 4 && !(dws->caps & DW_SPI_CAP_QUAD)) + return false; + + /* A multi-lane write requires Tx FIFO space after the control words */ + if (op->data.dir == SPI_MEM_DATA_OUT && op->data.buswidth > 1 && + dws->fifo_len <= DW_SPI_ENH_CTRL_ENTRIES) + 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) { unsigned int i, j, len; @@ -737,10 +927,15 @@ static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi) 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); - struct dw_spi_cfg cfg; + struct dw_spi_cfg cfg = {}; unsigned long flags; int ret; + // This path implements single-lane frames only + if (op->data.buswidth > 1 || op->addr.buswidth > 1 || + op->dummy.buswidth > 1 || op->cmd.buswidth > 1) + return -EOPNOTSUPP; + /* * Collect the outbound data into a single buffer to speed the * transmission up at least on the initial stage. @@ -764,7 +959,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) dw_spi_enable_chip(dws, 0); - dw_spi_update_config(dws, mem->spi, &cfg); + dw_spi_update_config(dws, mem->spi, &cfg, NULL); dw_spi_mask_intr(dws, 0xff); @@ -840,7 +1035,10 @@ 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; + if (dws->caps & DW_SPI_CAP_QUAD) + dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op; + else + 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; @@ -946,14 +1144,22 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws) /* * 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. + * with zero offset. The ENH_FRF field is probed the same way. */ if (dw_spi_ip_is(dws, PSSI)) { u32 cr0, tmp = dw_readl(dws, DW_SPI_CTRLR0); + u32 enh_frf = 0; dw_spi_enable_chip(dws, 0); + dw_writel(dws, DW_SPI_CTRLR0, 0xffffffff); cr0 = dw_readl(dws, DW_SPI_CTRLR0); + + /* ENH_FRF: quad is the only format the driver uses */ + dw_writel(dws, DW_SPI_CTRLR0, (tmp & ~DW_PSSI_CTRLR0_ENH_FRF_MASK) | + FIELD_PREP(DW_PSSI_CTRLR0_ENH_FRF_MASK, DW_SPI_CTRLR0_ENH_FRF_QUAD)); + enh_frf = FIELD_GET(DW_PSSI_CTRLR0_ENH_FRF_MASK, dw_readl(dws, DW_SPI_CTRLR0)); + dw_writel(dws, DW_SPI_CTRLR0, tmp); dw_spi_enable_chip(dws, 1); @@ -962,6 +1168,11 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws) dws->dfs_offset = __bf_shf(DW_PSSI_CTRLR0_DFS32_MASK); dev_dbg(dev, "Detected 32-bits max data frame size\n"); } + + if (enh_frf == DW_SPI_CTRLR0_ENH_FRF_QUAD) { + dws->caps |= DW_SPI_CAP_QUAD; + dev_dbg(dev, "Detected quad enhanced SPI mode\n"); + } } else { dws->caps |= DW_SPI_CAP_DFS32; } @@ -1008,6 +1219,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) host->use_gpio_descriptors = true; host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + if (dws->caps & DW_SPI_CAP_QUAD) + host->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD; if (dws->caps & DW_SPI_CAP_DFS32) host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index e52fa329c2476..819cf470bd8ff 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -540,7 +540,7 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, usleep_range(5, 10); /* Write something to the TX FIFO to start the transfer */ - dw_writel(dws, DW_SPI_DR, 0); + dw_spi_start_frame(dws); } ret = dw_spi_dma_wait(dws, xfer->len, xfer->effective_speed_hz); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2e7d8527d0a1c..c839ca15d1167 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -32,6 +32,7 @@ /* DW SPI controller capabilities */ #define DW_SPI_CAP_CS_OVERRIDE BIT(0) #define DW_SPI_CAP_DFS32 BIT(1) +#define DW_SPI_CAP_QUAD BIT(2) /* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */ #define DW_SPI_CTRLR0 0x00 @@ -62,6 +63,9 @@ #define DW_SPI_RX_SAMPLE_DLY 0xf0 #define DW_SPI_CS_OVERRIDE 0xf4 +/* SPI_CTRLR0, same address as CS_OVERRIDE (controller has only one of them) */ +#define DW_SPI_ENH_CTRLR0 0xf4 + /* 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) @@ -86,6 +90,11 @@ #define DW_PSSI_CTRLR0_SRL BIT(11) #define DW_PSSI_CTRLR0_CFS BIT(12) +#define DW_PSSI_CTRLR0_ENH_FRF_MASK GENMASK(22, 21) +#define DW_SPI_CTRLR0_ENH_FRF_STD 0x0 +#define DW_SPI_CTRLR0_ENH_FRF_DUAL 0x1 +#define DW_SPI_CTRLR0_ENH_FRF_QUAD 0x2 + /* 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) @@ -121,6 +130,18 @@ #define DW_SPI_DMACR_RDMAE BIT(0) #define DW_SPI_DMACR_TDMAE BIT(1) +/* Bit fields in ENH_CTRLR0 */ +#define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK GENMASK(15, 11) +#define DW_SPI_ENH_CTRLR0_INST_L_MASK GENMASK(9, 8) +#define DW_SPI_ENH_CTRLR0_INST_L_0BIT 0x0 +#define DW_SPI_ENH_CTRLR0_INST_L_8BIT 0x2 +#define DW_SPI_ENH_CTRLR0_INST_L_16BIT 0x3 +#define DW_SPI_ENH_CTRLR0_ADDR_L_MASK GENMASK(5, 2) +#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK GENMASK(1, 0) +#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0 0x0 /* inst std, addr std */ +#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1 0x1 /* inst std, addr enh */ +#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2 0x2 /* inst enh, addr enh */ + /* Mem/DMA operations helpers */ #define DW_SPI_WAIT_RETRIES 5 #define DW_SPI_BUF_SIZE \ @@ -128,15 +149,40 @@ sizeof_field(struct spi_mem_op, addr.val) + 256) #define DW_SPI_GET_BYTE(_val, _idx) \ ((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff) +/* + * Tx FIFO entries used by the control words. The controller reads INST_L + * bits from one entry and ADDR_L bits from the next. DFS does not apply. + */ +#define DW_SPI_ENH_CTRL_ENTRIES 2 /* Slave spi_transfer/spi_mem_op related */ struct dw_spi_cfg { u8 tmode; u8 dfs; + u8 enh_frf; /* CTRLR0 [22:21] */ u32 ndf; u32 freq; }; +/* The SPI_CTRLR0 layout of an enhanced frame */ +struct dw_spi_enh_cfg { + u8 trans_type; /* SPI_CTRLR0 [1:0] */ + u8 addr_l; /* SPI_CTRLR0 [5:2] */ + u8 inst_l; /* SPI_CTRLR0 [9:8] */ + u8 wait_l; /* SPI_CTRLR0 [15:11] */ +}; + +/* + * Enhanced frame being collected. cfg holds the SPI_CTRLR0 phase lengths. + * inst and addr are the two control words for the Tx FIFO. + */ +struct dw_spi_enh_frame { + struct dw_spi_enh_cfg cfg; + u32 inst; + u32 addr; + u8 ctrl_len; /* control Tx FIFO entries */ +}; + struct dw_spi; struct dw_spi_dma_ops { int (*dma_init)(struct device *dev, struct dw_spi *dws); @@ -176,6 +222,7 @@ struct dw_spi { u8 buf[DW_SPI_BUF_SIZE]; int dma_mapped; u8 n_bytes; /* current is a 1/2 bytes op */ + struct dw_spi_enh_frame enh; /* Enhanced frame */ irqreturn_t (*transfer_handler)(struct dw_spi *dws); u32 current_freq; /* frequency in hz */ u32 cur_rx_sample_dly; @@ -290,8 +337,10 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, - struct dw_spi_cfg *cfg); + struct dw_spi_cfg *cfg, + struct dw_spi_enh_cfg *ecfg); extern int dw_spi_check_status(struct dw_spi *dws, bool raw); +extern void dw_spi_start_frame(struct dw_spi *dws); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws);