u32 wait_mode;
u32 xfer_mode;
u32 prepend_cnt;
+ u32 md_start;
u8 *prepend_buf;
};
{
struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host);
- bool tx_only = false;
+ bool tx_only = false, multidata = false;
struct spi_transfer *t;
/*
* Multiple transfers within a message may be combined into one transfer
* to the controller using its prepend feature. A SPI message is prependable
* only if the following are all true:
- * 1. One or more half duplex write transfer in single bit mode
- * 2. Optional full duplex read/write at the end
- * 3. No delay and cs_change between transfers
+ * 1. One or more half duplex write transfers at the start
+ * 2. Optional switch from single to dual bit within the write transfers
+ * 3. Optional full duplex read/write at the end if all single bit
+ * 4. No delay and cs_change between transfers
*/
bs->prepend_cnt = 0;
+ bs->md_start = 0;
list_for_each_entry(t, &msg->transfers, transfer_list) {
if ((spi_delay_to_ns(&t->delay, t) > 0) || t->cs_change) {
bcm63xx_prepend_printk_on_checkfail(bs,
return false;
}
- if (t->tx_nbits > SPI_NBITS_SINGLE &&
- !list_is_last(&t->transfer_list, &msg->transfers)) {
+ if (t->tx_nbits == SPI_NBITS_SINGLE &&
+ !list_is_last(&t->transfer_list, &msg->transfers) &&
+ multidata) {
bcm63xx_prepend_printk_on_checkfail(bs,
- "multi-bit prepend buf not supported!\n");
+ "single-bit after multi-bit not supported!\n");
return false;
}
- if (t->tx_nbits == SPI_NBITS_SINGLE) {
- memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, t->len);
- bs->prepend_cnt += t->len;
- }
+ if (t->tx_nbits > SPI_NBITS_SINGLE)
+ multidata = true;
+
+ memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, t->len);
+ bs->prepend_cnt += t->len;
+
+ if (t->tx_nbits == SPI_NBITS_SINGLE)
+ bs->md_start += t->len;
+
} else {
if (!list_is_last(&t->transfer_list, &msg->transfers)) {
bcm63xx_prepend_printk_on_checkfail(bs,
"rx/tx_rx transfer not supported when it is not last one!\n");
return false;
}
+
+ if (t->rx_buf && t->rx_nbits == SPI_NBITS_SINGLE &&
+ multidata) {
+ bcm63xx_prepend_printk_on_checkfail(bs,
+ "single-bit after multi-bit not supported!\n");
+ return false;
+ }
}
if (list_is_last(&t->transfer_list, &msg->transfers)) {
memcpy(t_prepend, t, sizeof(struct spi_transfer));
- if (tx_only && t->tx_nbits == SPI_NBITS_SINGLE) {
+ if (tx_only) {
/*
- * if the last one is also a single bit tx only transfer, merge
+ * if the last one is also a tx only transfer, merge
* all of them into one single tx transfer
*/
t_prepend->len = bs->prepend_cnt;
bs->prepend_cnt = 0;
} else {
/*
- * if the last one is not a tx only transfer or dual tx xfer, all
+ * if the last one is not a tx only transfer, all
* the previous transfers are sent through prepend bytes and
* make sure it does not exceed the max prepend len
*/
return false;
}
}
+ /*
+ * If switching from single-bit to multi-bit, make sure
+ * the start offset does not exceed the maximum
+ */
+ if (multidata && bs->md_start > HSSPI_MAX_PREPEND_LEN) {
+ bcm63xx_prepend_printk_on_checkfail(bs,
+ "exceed max multi-bit offset, abort prepending transfers!\n");
+ return false;
+ }
}
}
if (t->rx_nbits == SPI_NBITS_DUAL) {
reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT;
- reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT;
+ reg |= bs->md_start << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT;
}
if (t->tx_nbits == SPI_NBITS_DUAL) {
reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT;
- reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT;
+ reg |= bs->md_start << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT;
}
}
if (!spi_mem_default_supports_op(mem, op))
return false;
- /* Controller doesn't support spi mem dual io mode */
- if ((op->cmd.opcode == SPINOR_OP_READ_1_2_2) ||
- (op->cmd.opcode == SPINOR_OP_READ_1_2_2_4B) ||
- (op->cmd.opcode == SPINOR_OP_READ_1_2_2_DTR) ||
- (op->cmd.opcode == SPINOR_OP_READ_1_2_2_DTR_4B))
- return false;
-
return true;
}