#define SPI_NOR_MAX_ADDR_WIDTH 4
 
+/**
+ * spi_nor_get_cmd_ext() - Get the command opcode extension based on the
+ *                        extension type.
+ * @nor:               pointer to a 'struct spi_nor'
+ * @op:                        pointer to the 'struct spi_mem_op' whose properties
+ *                     need to be initialized.
+ *
+ * Right now, only "repeat" and "invert" are supported.
+ *
+ * Return: The opcode extension.
+ */
+static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor,
+                             const struct spi_mem_op *op)
+{
+       switch (nor->cmd_ext_type) {
+       case SPI_NOR_EXT_INVERT:
+               return ~op->cmd.opcode;
+
+       case SPI_NOR_EXT_REPEAT:
+               return op->cmd.opcode;
+
+       default:
+               dev_err(nor->dev, "Unknown command extension type\n");
+               return 0;
+       }
+}
+
+/**
+ * spi_nor_spimem_setup_op() - Set up common properties of a spi-mem op.
+ * @nor:               pointer to a 'struct spi_nor'
+ * @op:                        pointer to the 'struct spi_mem_op' whose properties
+ *                     need to be initialized.
+ * @proto:             the protocol from which the properties need to be set.
+ */
+void spi_nor_spimem_setup_op(const struct spi_nor *nor,
+                            struct spi_mem_op *op,
+                            const enum spi_nor_protocol proto)
+{
+       u8 ext;
+
+       op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto);
+
+       if (op->addr.nbytes)
+               op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto);
+
+       if (op->dummy.nbytes)
+               op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto);
+
+       if (op->data.nbytes)
+               op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
+
+       if (spi_nor_protocol_is_dtr(proto)) {
+               /*
+                * SPIMEM supports mixed DTR modes, but right now we can only
+                * have all phases either DTR or STR. IOW, SPIMEM can have
+                * something like 4S-4D-4D, but SPI NOR can't. So, set all 4
+                * phases to either DTR or STR.
+                */
+               op->cmd.dtr = true;
+               op->addr.dtr = true;
+               op->dummy.dtr = true;
+               op->data.dtr = true;
+
+               /* 2 bytes per clock cycle in DTR mode. */
+               op->dummy.nbytes *= 2;
+
+               ext = spi_nor_get_cmd_ext(nor, op);
+               op->cmd.opcode = (op->cmd.opcode << 8) | ext;
+               op->cmd.nbytes = 2;
+       }
+}
+
 /**
  * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data
  *                           transfer
 static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
                                           u8 *buf, size_t len)
 {
+       if (spi_nor_protocol_is_dtr(nor->reg_proto))
+               return -EOPNOTSUPP;
+
        return nor->controller_ops->read_reg(nor, opcode, buf, len);
 }
 
 static int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
                                            const u8 *buf, size_t len)
 {
+       if (spi_nor_protocol_is_dtr(nor->reg_proto))
+               return -EOPNOTSUPP;
+
        return nor->controller_ops->write_reg(nor, opcode, buf, len);
 }
 
 static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs)
 {
+       if (spi_nor_protocol_is_dtr(nor->write_proto))
+               return -EOPNOTSUPP;
+
        return nor->controller_ops->erase(nor, offs);
 }
 
                                        size_t len, u8 *buf)
 {
        struct spi_mem_op op =
-               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
-                          SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
-                          SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
-                          SPI_MEM_OP_DATA_IN(len, buf, 1));
+               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
+                          SPI_MEM_OP_ADDR(nor->addr_width, from, 0),
+                          SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
+                          SPI_MEM_OP_DATA_IN(len, buf, 0));
        bool usebouncebuf;
        ssize_t nbytes;
        int error;
 
-       /* get transfer protocols. */
-       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-       op.dummy.buswidth = op.addr.buswidth;
-       op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+       spi_nor_spimem_setup_op(nor, &op, nor->read_proto);
 
        /* convert the dummy cycles to the number of bytes */
        op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+       if (spi_nor_protocol_is_dtr(nor->read_proto))
+               op.dummy.nbytes *= 2;
 
        usebouncebuf = spi_nor_spimem_bounce(nor, &op);
 
                                         size_t len, const u8 *buf)
 {
        struct spi_mem_op op =
-               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
-                          SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
+               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
+                          SPI_MEM_OP_ADDR(nor->addr_width, to, 0),
                           SPI_MEM_OP_NO_DUMMY,
-                          SPI_MEM_OP_DATA_OUT(len, buf, 1));
+                          SPI_MEM_OP_DATA_OUT(len, buf, 0));
        ssize_t nbytes;
        int error;
 
-       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-       op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
        if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
                op.addr.nbytes = 0;
 
+       spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
+
        if (spi_nor_spimem_bounce(nor, &op))
                memcpy(nor->bouncebuf, buf, op.data.nbytes);
 
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
                                   SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREN,
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
                                   SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRDI,
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, sr, 1));
+                                  SPI_MEM_OP_DATA_IN(1, sr, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, fsr, 1));
+                                  SPI_MEM_OP_DATA_IN(1, fsr, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, cr, 1));
+                                  SPI_MEM_OP_DATA_IN(1, cr, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                        SPI_MEM_OP(SPI_MEM_OP_CMD(enable ?
                                                  SPINOR_OP_EN4B :
                                                  SPINOR_OP_EX4B,
-                                                 1),
+                                                 0),
                                  SPI_MEM_OP_NO_ADDR,
                                  SPI_MEM_OP_NO_DUMMY,
                                  SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                ret = spi_nor_controller_ops_write_reg(nor,
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, sr, 1));
+                                  SPI_MEM_OP_DATA_IN(1, sr, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
                                   SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR,
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
                                   SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR,
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(len, sr, 1));
+                                  SPI_MEM_OP_DATA_OUT(len, sr, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, sr2, 1));
+                                  SPI_MEM_OP_DATA_OUT(1, sr2, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, sr2, 1));
+                                  SPI_MEM_OP_DATA_IN(1, sr2, 0));
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0),
                                   SPI_MEM_OP_NO_ADDR,
                                   SPI_MEM_OP_NO_DUMMY,
                                   SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
+
                ret = spi_mem_exec_op(nor->spimem, &op);
        } else {
                ret = spi_nor_controller_ops_write_reg(nor,
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1),
-                                  SPI_MEM_OP_ADDR(nor->addr_width, addr, 1),
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0),
+                                  SPI_MEM_OP_ADDR(nor->addr_width, addr, 0),
                                   SPI_MEM_OP_NO_DUMMY,
                                   SPI_MEM_OP_NO_DATA);
 
+               spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
+
                return spi_mem_exec_op(nor->spimem, &op);
        } else if (nor->controller_ops->erase) {
                return spi_nor_controller_ops_erase(nor, addr);
                { SNOR_HWCAPS_READ_1_8_8,       SNOR_CMD_READ_1_8_8 },
                { SNOR_HWCAPS_READ_8_8_8,       SNOR_CMD_READ_8_8_8 },
                { SNOR_HWCAPS_READ_1_8_8_DTR,   SNOR_CMD_READ_1_8_8_DTR },
+               { SNOR_HWCAPS_READ_8_8_8_DTR,   SNOR_CMD_READ_8_8_8_DTR },
        };
 
        return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
                { SNOR_HWCAPS_PP_1_1_8,         SNOR_CMD_PP_1_1_8 },
                { SNOR_HWCAPS_PP_1_8_8,         SNOR_CMD_PP_1_8_8 },
                { SNOR_HWCAPS_PP_8_8_8,         SNOR_CMD_PP_8_8_8 },
+               { SNOR_HWCAPS_PP_8_8_8_DTR,     SNOR_CMD_PP_8_8_8_DTR },
        };
 
        return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
 static int spi_nor_spimem_check_readop(struct spi_nor *nor,
                                       const struct spi_nor_read_command *read)
 {
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1),
-                                         SPI_MEM_OP_ADDR(3, 0, 1),
-                                         SPI_MEM_OP_DUMMY(0, 1),
-                                         SPI_MEM_OP_DATA_IN(0, NULL, 1));
+       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0),
+                                         SPI_MEM_OP_ADDR(3, 0, 0),
+                                         SPI_MEM_OP_DUMMY(1, 0),
+                                         SPI_MEM_OP_DATA_IN(1, NULL, 0));
 
-       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto);
-       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto);
-       op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto);
-       op.dummy.buswidth = op.addr.buswidth;
-       op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
-                         op.dummy.buswidth / 8;
+       spi_nor_spimem_setup_op(nor, &op, read->proto);
+
+       /* convert the dummy cycles to the number of bytes */
+       op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+       if (spi_nor_protocol_is_dtr(nor->read_proto))
+               op.dummy.nbytes *= 2;
 
        return spi_nor_spimem_check_op(nor, &op);
 }
 static int spi_nor_spimem_check_pp(struct spi_nor *nor,
                                   const struct spi_nor_pp_command *pp)
 {
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1),
-                                         SPI_MEM_OP_ADDR(3, 0, 1),
+       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0),
+                                         SPI_MEM_OP_ADDR(3, 0, 0),
                                          SPI_MEM_OP_NO_DUMMY,
-                                         SPI_MEM_OP_DATA_OUT(0, NULL, 1));
+                                         SPI_MEM_OP_DATA_OUT(1, NULL, 0));
 
-       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto);
-       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto);
-       op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto);
+       spi_nor_spimem_setup_op(nor, &op, pp->proto);
 
        return spi_nor_spimem_check_op(nor, &op);
 }
        struct spi_nor_flash_parameter *params = nor->params;
        unsigned int cap;
 
-       /* DTR modes are not supported yet, mask them all. */
-       *hwcaps &= ~SNOR_HWCAPS_DTR;
-
        /* X-X-X modes are not supported yet, mask them all. */
        *hwcaps &= ~SNOR_HWCAPS_X_X_X;
 
+       /*
+        * If the reset line is broken, we do not want to enter a stateful
+        * mode.
+        */
+       if (nor->flags & SNOR_F_BROKEN_RESET)
+               *hwcaps &= ~(SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR);
+
        for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) {
                int rdidx, ppidx;
 
                 * controller directly implements the spi_nor interface.
                 * Yet another reason to switch to spi-mem.
                 */
-               ignored_mask = SNOR_HWCAPS_X_X_X;
+               ignored_mask = SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR;
                if (shared_mask & ignored_mask) {
                        dev_dbg(nor->dev,
                                "SPI n-n-n protocols are not supported.\n");
                                          SNOR_PROTO_1_1_8);
        }
 
+       if (info->flags & SPI_NOR_OCTAL_DTR_READ) {
+               params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
+               spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR],
+                                         0, 20, SPINOR_OP_READ_FAST,
+                                         SNOR_PROTO_8_8_8_DTR);
+       }
+
        /* Page Program settings. */
        params->hwcaps.mask |= SNOR_HWCAPS_PP;
        spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP],
                                SPINOR_OP_PP, SNOR_PROTO_1_1_1);
 
+       if (info->flags & SPI_NOR_OCTAL_DTR_PP) {
+               params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+               /*
+                * Since xSPI Page Program opcode is backward compatible with
+                * Legacy SPI, use Legacy SPI opcode there as well.
+                */
+               spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR],
+                                       SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR);
+       }
+
        /*
         * Sector Erase settings. Sort Erase Types in ascending order, with the
         * smallest erase size starting at BIT(0).
 
        spi_nor_manufacturer_init_params(nor);
 
-       if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
+       if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                                SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ)) &&
            !(nor->info->flags & SPI_NOR_SKIP_SFDP))
                spi_nor_sfdp_init_params(nor);
 
                return err;
        }
 
-       if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
+       if (nor->addr_width == 4 &&
+           nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
+           !(nor->flags & SNOR_F_4B_OPCODES)) {
                /*
                 * If the RESET# pin isn't hooked up properly, or the system
                 * otherwise doesn't perform a reset command in the boot
 {
        if (nor->addr_width) {
                /* already configured from SFDP */
+       } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) {
+               /*
+                * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So
+                * in this protocol an odd address width cannot be used because
+                * then the address phase would only span a cycle and a half.
+                * Half a cycle would be left over. We would then have to start
+                * the dummy phase in the middle of a cycle and so too the data
+                * phase, and we will end the transaction with half a cycle left
+                * over.
+                *
+                * Force all 8D-8D-8D flashes to use an address width of 4 to
+                * avoid this situation.
+                */
+               nor->addr_width = 4;
        } else if (nor->info->addr_width) {
                nor->addr_width = nor->info->addr_width;
        } else {
 static int spi_nor_create_read_dirmap(struct spi_nor *nor)
 {
        struct spi_mem_dirmap_info info = {
-               .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
-                                     SPI_MEM_OP_ADDR(nor->addr_width, 0, 1),
-                                     SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
-                                     SPI_MEM_OP_DATA_IN(0, NULL, 1)),
+               .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
+                                     SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
+                                     SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
+                                     SPI_MEM_OP_DATA_IN(0, NULL, 0)),
                .offset = 0,
                .length = nor->mtd.size,
        };
        struct spi_mem_op *op = &info.op_tmpl;
 
-       /* get transfer protocols. */
-       op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-       op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-       op->dummy.buswidth = op->addr.buswidth;
-       op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+       spi_nor_spimem_setup_op(nor, op, nor->read_proto);
 
        /* convert the dummy cycles to the number of bytes */
        op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
+       if (spi_nor_protocol_is_dtr(nor->read_proto))
+               op->dummy.nbytes *= 2;
+
+       /*
+        * Since spi_nor_spimem_setup_op() only sets buswidth when the number
+        * of data bytes is non-zero, the data buswidth won't be set here. So,
+        * do it explicitly.
+        */
+       op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
 
        nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
                                                       &info);
 static int spi_nor_create_write_dirmap(struct spi_nor *nor)
 {
        struct spi_mem_dirmap_info info = {
-               .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
-                                     SPI_MEM_OP_ADDR(nor->addr_width, 0, 1),
+               .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
+                                     SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
                                      SPI_MEM_OP_NO_DUMMY,
-                                     SPI_MEM_OP_DATA_OUT(0, NULL, 1)),
+                                     SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
                .offset = 0,
                .length = nor->mtd.size,
        };
        struct spi_mem_op *op = &info.op_tmpl;
 
-       /* get transfer protocols. */
-       op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-       op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-       op->dummy.buswidth = op->addr.buswidth;
-       op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
        if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
                op->addr.nbytes = 0;
 
+       spi_nor_spimem_setup_op(nor, op, nor->write_proto);
+
+       /*
+        * Since spi_nor_spimem_setup_op() only sets buswidth when the number
+        * of data bytes is non-zero, the data buswidth won't be set here. So,
+        * do it explicitly.
+        */
+       op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
        nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
                                                       &info);
        return PTR_ERR_OR_ZERO(nor->dirmap.wdesc);