aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2024-05-24 09:10:54 -0600
committerTom Rini <trini@konsulko.com>2024-05-24 09:10:54 -0600
commit5e625dca4ed7fcb8aedb4b6400aeaf8a19bed856 (patch)
tree66215f2b04e24864a1cbe5ab61235913b44640e3
parent377e91c162ab09ec20f96f966f380cb55c590edd (diff)
parent75eab7c61c4ff729f11d5b99bef831b5cd9b7f0c (diff)
downloadu-boot-WIP/24May2024-next.zip
u-boot-WIP/24May2024-next.tar.gz
u-boot-WIP/24May2024-next.tar.bz2
Merge patch series "spi-nor: Add parallel and stacked memories support"WIP/24May2024-next
Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com> says: This series adds support for Xilinx qspi parallel and stacked memeories. In parallel mode, the current implementation assumes that a maximum of two flashes are connected. The QSPI controller splits the data evenly between both the flashes so, both the flashes that are connected in parallel mode should be identical. During each operation SPI-NOR sets 0th bit for CS0 & 1st bit for CS1 in nor->flags. In stacked mode the current implementation assumes that a maximum of two flashes are connected and both the flashes are of same make but can differ in sizes. So, except the sizes all other flash parameters of both the flashes are identical. Spi-nor will pass on the appropriate flash select flag to low level driver, and it will select pass all the data to that particular flash. Write operation in parallel mode are performed in page size * 2 chunks as each write operation results in writing both the flashes. For doubling the address space each operation is performed at addr/2 flash offset, where addr is the address specified by the user. Similarly for read and erase operations it will read from both flashes, so size and offset are divided by 2 and send to flash.
-rw-r--r--configs/imx28_xea_defconfig1
-rw-r--r--drivers/mtd/spi/sandbox.c2
-rw-r--r--drivers/mtd/spi/spi-nor-core.c399
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/altera_spi.c4
-rw-r--r--drivers/spi/atcspi200_spi.c2
-rw-r--r--drivers/spi/ath79_spi.c2
-rw-r--r--drivers/spi/atmel_spi.c6
-rw-r--r--drivers/spi/bcm63xx_hsspi.c42
-rw-r--r--drivers/spi/bcm63xx_spi.c6
-rw-r--r--drivers/spi/bcmbca_hsspi.c34
-rw-r--r--drivers/spi/cf_spi.c6
-rw-r--r--drivers/spi/davinci_spi.c8
-rw-r--r--drivers/spi/fsl_dspi.c18
-rw-r--r--drivers/spi/fsl_espi.c4
-rw-r--r--drivers/spi/fsl_qspi.c4
-rw-r--r--drivers/spi/gxp_spi.c2
-rw-r--r--drivers/spi/mpc8xx_spi.c4
-rw-r--r--drivers/spi/mpc8xxx_spi.c10
-rw-r--r--drivers/spi/mscc_bb_spi.c4
-rw-r--r--drivers/spi/mxc_spi.c6
-rw-r--r--drivers/spi/npcm_fiu_spi.c14
-rw-r--r--drivers/spi/nxp_fspi.c2
-rw-r--r--drivers/spi/octeon_spi.c2
-rw-r--r--drivers/spi/omap3_spi.c4
-rw-r--r--drivers/spi/pic32_spi.c2
-rw-r--r--drivers/spi/rk_spi.c4
-rw-r--r--drivers/spi/rockchip_sfc.c2
-rw-r--r--drivers/spi/spi-aspeed-smc.c28
-rw-r--r--drivers/spi/spi-mxic.c6
-rw-r--r--drivers/spi/spi-qup.c4
-rw-r--r--drivers/spi/spi-sifive.c6
-rw-r--r--drivers/spi/spi-sn-f-ospi.c2
-rw-r--r--drivers/spi/spi-sunxi.c6
-rw-r--r--drivers/spi/spi-synquacer.c4
-rw-r--r--drivers/spi/spi-uclass.c34
-rw-r--r--drivers/spi/stm32_qspi.c2
-rw-r--r--drivers/spi/stm32_spi.c4
-rw-r--r--drivers/spi/ti_qspi.c14
-rw-r--r--drivers/spi/xilinx_spi.c6
-rw-r--r--drivers/spi/zynq_qspi.c119
-rw-r--r--drivers/spi/zynq_spi.c6
-rw-r--r--drivers/spi/zynqmp_gqspi.c141
-rw-r--r--include/linux/mtd/spi-nor.h12
-rw-r--r--include/spi.h23
-rw-r--r--lib/acpi/acpi_device.c2
46 files changed, 792 insertions, 228 deletions
diff --git a/configs/imx28_xea_defconfig b/configs/imx28_xea_defconfig
index 822a329..8008e4c 100644
--- a/configs/imx28_xea_defconfig
+++ b/configs/imx28_xea_defconfig
@@ -126,6 +126,7 @@ CONFIG_SPECIFY_CONSOLE_INDEX=y
CONFIG_DM_SERIAL=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
+# CONFIG_SPI_ADVANCE is not set
CONFIG_MXS_SPI=y
CONFIG_SPL_CRC8=y
# CONFIG_SPL_OF_LIBFDT is not set
diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c
index 2d5a16b..e5ebc34 100644
--- a/drivers/mtd/spi/sandbox.c
+++ b/drivers/mtd/spi/sandbox.c
@@ -138,7 +138,7 @@ static int sandbox_sf_probe(struct udevice *dev)
return ret;
}
slave_plat = dev_get_parent_plat(dev);
- cs = slave_plat->cs;
+ cs = slave_plat->cs[0];
debug("found at cs %d\n", cs);
if (!pdata->filename) {
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 982dd25..ce4b062 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -461,8 +461,9 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
}
/*
- * Read the status register, returning its value in the location
- * Return the status register value.
+ * Return the status register value. If the chip is parallel, then the
+ * read will be striped, so we should read 2 bytes to get the sr
+ * register value from both of the parallel chips.
* Returns negative if error occurred.
*/
static int read_sr(struct spi_nor *nor)
@@ -494,18 +495,29 @@ static int read_sr(struct spi_nor *nor)
if (spi_nor_protocol_is_dtr(nor->reg_proto))
op.data.nbytes = 2;
- ret = spi_nor_read_write_reg(nor, &op, val);
- if (ret < 0) {
- pr_debug("error %d reading SR\n", (int)ret);
- return ret;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ op.data.nbytes = 2;
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading SR\n", (int)ret);
+ return ret;
+ }
+ val[0] |= val[1];
+ } else {
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading SR\n", (int)ret);
+ return ret;
+ }
}
- return *val;
+ return val[0];
}
/*
- * Read the flag status register, returning its value in the location
- * Return the status register value.
+ * Return the flag status register value. If the chip is parallel, then
+ * the read will be striped, so we should read 2 bytes to get the fsr
+ * register value from both of the parallel chips.
* Returns negative if error occurred.
*/
static int read_fsr(struct spi_nor *nor)
@@ -537,13 +549,23 @@ static int read_fsr(struct spi_nor *nor)
if (spi_nor_protocol_is_dtr(nor->reg_proto))
op.data.nbytes = 2;
- ret = spi_nor_read_write_reg(nor, &op, val);
- if (ret < 0) {
- pr_debug("error %d reading FSR\n", ret);
- return ret;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ op.data.nbytes = 2;
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading SR\n", (int)ret);
+ return ret;
+ }
+ val[0] &= val[1];
+ } else {
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading FSR\n", ret);
+ return ret;
+ }
}
- return *val;
+ return val[0];
}
/*
@@ -662,12 +684,17 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode)
static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
const struct flash_info *info)
{
+ bool shift = 0;
+
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ shift = 1;
+
/* Do some manufacturer fixups first */
switch (JEDEC_MFR(info)) {
case SNOR_MFR_SPANSION:
/* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE;
- nor->mtd.erasesize = info->sector_size;
+ nor->mtd.erasesize = info->sector_size << shift;
break;
default:
@@ -881,12 +908,32 @@ static int clean_bar(struct spi_nor *nor)
static int write_bar(struct spi_nor *nor, u32 offset)
{
- u8 cmd, bank_sel;
+ u8 cmd, bank_sel, upage_curr;
int ret;
+ struct mtd_info *mtd = &nor->mtd;
+
+ /* Wait until previous write command is finished */
+ if (spi_nor_wait_till_ready(nor))
+ return 1;
+
+ if (nor->flags & (SNOR_F_HAS_PARALLEL | SNOR_F_HAS_STACKED) &&
+ mtd->size <= SZ_32M)
+ return 0;
+
+ if (mtd->size <= SZ_16M)
+ return 0;
+
+ offset = offset % (u32)mtd->size;
+ bank_sel = offset >> 24;
- bank_sel = offset / SZ_16M;
- if (bank_sel == nor->bank_curr)
- goto bar_end;
+ upage_curr = nor->spi->flags & SPI_XFER_U_PAGE;
+
+ if (!(nor->flags & SNOR_F_HAS_STACKED) && bank_sel == nor->bank_curr)
+ return 0;
+ else if (upage_curr == nor->upage_prev && bank_sel == nor->bank_curr)
+ return 0;
+
+ nor->upage_prev = upage_curr;
cmd = nor->bank_write_cmd;
write_enable(nor);
@@ -896,15 +943,19 @@ static int write_bar(struct spi_nor *nor, u32 offset)
return ret;
}
-bar_end:
nor->bank_curr = bank_sel;
- return nor->bank_curr;
+
+ return write_disable(nor);
}
static int read_bar(struct spi_nor *nor, const struct flash_info *info)
{
u8 curr_bank = 0;
int ret;
+ struct mtd_info *mtd = &nor->mtd;
+
+ if (mtd->size <= SZ_16M)
+ return 0;
switch (JEDEC_MFR(info)) {
case SNOR_MFR_SPANSION:
@@ -916,15 +967,30 @@ static int read_bar(struct spi_nor *nor, const struct flash_info *info)
nor->bank_write_cmd = SPINOR_OP_WREAR;
}
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_XFER_LOWER;
+
ret = nor->read_reg(nor, nor->bank_read_cmd,
- &curr_bank, 1);
+ &curr_bank, 1);
if (ret) {
debug("SF: fail to read bank addr register\n");
return ret;
}
nor->bank_curr = curr_bank;
- return 0;
+ // Make sure both chips use the same BAR
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ write_enable(nor);
+ ret = nor->write_reg(nor, nor->bank_write_cmd, &curr_bank, 1);
+ if (ret)
+ return ret;
+
+ ret = write_disable(nor);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
}
#endif
@@ -988,8 +1054,8 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ u32 addr, len, rem, offset;
bool addr_known = false;
- u32 addr, len, rem;
int ret, err;
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
@@ -1014,6 +1080,19 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
ret = -EINTR;
goto erase_err;
}
+
+ offset = addr;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ offset /= 2;
+
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (offset >= (mtd->size / 2)) {
+ offset = offset - (mtd->size / 2);
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ }
#ifdef CONFIG_SPI_FLASH_BAR
ret = write_bar(nor, addr);
if (ret < 0)
@@ -1419,6 +1498,9 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
u8 id[SPI_NOR_MAX_ID_LEN];
const struct flash_info *info;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_XFER_LOWER;
+
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);
@@ -1443,28 +1525,67 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
int ret;
+ loff_t offset = from;
+ u32 read_len = 0;
+ u32 rem_bank_len = 0;
+ u8 bank;
+ bool is_ofst_odd = false;
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
+ if ((nor->flags & SNOR_F_HAS_PARALLEL) && (offset & 1)) {
+ /* We can hit this case when we use file system like ubifs */
+ from--;
+ len++;
+ is_ofst_odd = true;
+ }
+
while (len) {
- loff_t addr = from;
- size_t read_len = len;
+ if (nor->addr_width == 3) {
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ bank = (u32)from / (SZ_16M << 0x01);
+ rem_bank_len = ((SZ_16M << 0x01) *
+ (bank + 1)) - from;
+ } else {
+ bank = (u32)from / SZ_16M;
+ rem_bank_len = (SZ_16M * (bank + 1)) - from;
+ }
+ }
+ offset = from;
+
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (offset >= (mtd->size / 2)) {
+ offset = offset - (mtd->size / 2);
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ }
-#ifdef CONFIG_SPI_FLASH_BAR
- u32 remain_len;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ offset /= 2;
- ret = write_bar(nor, addr);
- if (ret < 0)
- return log_ret(ret);
- remain_len = (SZ_16M * (nor->bank_curr + 1)) - addr;
+ if (nor->addr_width == 3) {
+#ifdef CONFIG_SPI_FLASH_BAR
+ ret = write_bar(nor, offset);
+ if (ret < 0)
+ return log_ret(ret);
+#endif
+ }
- if (len < remain_len)
+ if (len < rem_bank_len)
read_len = len;
else
- read_len = remain_len;
-#endif
+ read_len = rem_bank_len;
+
+ if (read_len == 0)
+ return -EIO;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto read_err;
- ret = nor->read(nor, addr, read_len, buf);
+ ret = nor->read(nor, offset, read_len, buf);
if (ret == 0) {
/* We shouldn't see 0-length reads */
ret = -EIO;
@@ -1473,8 +1594,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
if (ret < 0)
goto read_err;
- *retlen += ret;
- buf += ret;
+ if (is_ofst_odd == true) {
+ memmove(buf, (buf + 1), (len - 1));
+ *retlen += (ret - 1);
+ buf += ret - 1;
+ is_ofst_odd = false;
+ } else {
+ *retlen += ret;
+ buf += ret;
+ }
from += ret;
len -= ret;
}
@@ -1769,6 +1897,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
struct spi_nor *nor = mtd_to_spi_nor(mtd);
size_t page_offset, page_remain, i;
ssize_t ret;
+ u32 offset;
#ifdef CONFIG_SPI_FLASH_SST
/* sst nor chips use AAI word program */
@@ -1778,6 +1907,27 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
+ if (!len)
+ return 0;
+
+ /*
+ * Cannot write to odd offset in parallel mode,
+ * so write 2 bytes first
+ */
+ if ((nor->flags & SNOR_F_HAS_PARALLEL) && (to & 1)) {
+ u8 two[2] = {0xff, buf[0]};
+ size_t local_retlen;
+
+ ret = spi_nor_write(mtd, to & ~1, 2, &local_retlen, two);
+ if (ret < 0)
+ return ret;
+
+ *retlen += 1; /* We've written only one actual byte */
+ ++buf;
+ --len;
+ ++to;
+ }
+
for (i = 0; i < len; ) {
ssize_t written;
loff_t addr = to + i;
@@ -1795,17 +1945,37 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
page_offset = do_div(aux, nor->page_size);
}
- /* the size of data remaining on the first page */
- page_remain = min_t(size_t,
- nor->page_size - page_offset, len - i);
+ offset = (to + i);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ offset /= 2;
+
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (offset >= (mtd->size / 2)) {
+ offset = offset - (mtd->size / 2);
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ }
+
+ if (nor->addr_width == 3) {
#ifdef CONFIG_SPI_FLASH_BAR
- ret = write_bar(nor, addr);
- if (ret < 0)
- return ret;
+ ret = write_bar(nor, offset);
+ if (ret < 0)
+ return ret;
#endif
+ }
+
+ page_remain = min_t(size_t, nor->page_size - page_offset,
+ len - i);
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto write_err;
+
write_enable(nor);
- ret = nor->write(nor, addr, page_remain, buf + i);
+ ret = nor->write(nor, offset, page_remain, buf + i);
if (ret < 0)
goto write_err;
written = ret;
@@ -1813,6 +1983,11 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto write_err;
+
+ ret = write_disable(nor);
+ if (ret)
+ goto write_err;
+
*retlen += written;
i += written;
}
@@ -1853,6 +2028,10 @@ static int macronix_quad_enable(struct spi_nor *nor)
if (ret)
return ret;
+ ret = write_disable(nor);
+ if (ret)
+ return ret;
+
ret = read_sr(nor);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
dev_err(nor->dev, "Macronix Quad bit not set\n");
@@ -1914,7 +2093,7 @@ static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base,
return -EINVAL;
}
- return 0;
+ return write_disable(nor);
}
#endif
@@ -2090,6 +2269,10 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
nor->read_dummy = 8;
while (len) {
+ /* Both chips are identical, so should be the SFDP data */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_XFER_LOWER;
+
ret = nor->read(nor, addr, len, (u8 *)buf);
if (!ret || ret > len) {
ret = -EIO;
@@ -2784,6 +2967,13 @@ static int spi_nor_init_params(struct spi_nor *nor,
const struct flash_info *info,
struct spi_nor_flash_parameter *params)
{
+#if CONFIG_IS_ENABLED(DM_SPI)
+ struct udevice *dev = nor->spi->dev;
+ u64 flash_size[SNOR_FLASH_CNT_MAX] = {0};
+ u32 idx = 0, i = 0;
+ int rc;
+#endif
+
/* Set legacy flash parameters as default. */
memset(params, 0, sizeof(*params));
@@ -2902,6 +3092,62 @@ static int spi_nor_init_params(struct spi_nor *nor,
}
}
+#if CONFIG_IS_ENABLED(DM_SPI)
+ /*
+ * The flashes that are connected in stacked mode should be of same make.
+ * Except the flash size all other properties are identical for all the
+ * flashes connected in stacked mode.
+ * The flashes that are connected in parallel mode should be identical.
+ */
+ while (i < SNOR_FLASH_CNT_MAX) {
+ rc = ofnode_read_u64_index(dev_ofnode(dev), "stacked-memories",
+ idx, &flash_size[i]);
+ if (rc == -EINVAL) {
+ break;
+ } else if (rc == -EOVERFLOW) {
+ idx++;
+ } else {
+ idx++;
+ i++;
+ if (!(nor->flags & SNOR_F_HAS_STACKED))
+ nor->flags |= SNOR_F_HAS_STACKED;
+ if (!(nor->spi->flags & SPI_XFER_STACKED))
+ nor->spi->flags |= SPI_XFER_STACKED;
+ }
+ }
+
+ i = 0;
+ idx = 0;
+ while (i < SNOR_FLASH_CNT_MAX) {
+ rc = ofnode_read_u64_index(dev_ofnode(dev), "parallel-memories",
+ idx, &flash_size[i]);
+ if (rc == -EINVAL) {
+ break;
+ } else if (rc == -EOVERFLOW) {
+ idx++;
+ } else {
+ idx++;
+ i++;
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL))
+ nor->flags |= SNOR_F_HAS_PARALLEL;
+ }
+ }
+
+ if (nor->flags & (SNOR_F_HAS_STACKED | SNOR_F_HAS_PARALLEL)) {
+ params->size = 0;
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++)
+ params->size += flash_size[idx];
+ }
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ nor->mtd.erasesize <<= 1;
+ params->page_size <<= 1;
+ }
+#endif
spi_nor_post_sfdp_fixups(nor, params);
return 0;
@@ -3216,16 +3462,54 @@ static int spi_nor_select_erase(struct spi_nor *nor,
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
nor->erase_opcode = SPINOR_OP_BE_4K;
- mtd->erasesize = 4096;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = 4096 * 2;
+ else
+ mtd->erasesize = 4096;
} else if (info->flags & SECT_4K_PMC) {
nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
- mtd->erasesize = 4096;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = 4096 * 2;
+ else
+ mtd->erasesize = 4096;
} else
#endif
{
nor->erase_opcode = SPINOR_OP_SE;
- mtd->erasesize = info->sector_size;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = info->sector_size * 2;
+ else
+ mtd->erasesize = info->sector_size;
+ }
+
+ if ((JEDEC_MFR(info) == SNOR_MFR_SST) && info->flags & SECT_4K) {
+ nor->erase_opcode = SPINOR_OP_BE_4K;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = 4096 * 2;
+ else
+ mtd->erasesize = 4096;
}
+
return 0;
}
@@ -3847,6 +4131,9 @@ static int spi_nor_init(struct spi_nor *nor)
{
int err;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_NOR_ENABLE_MULTI_CS;
+
err = spi_nor_octal_dtr_enable(nor);
if (err) {
dev_dbg(nor->dev, "Octal DTR mode not supported\n");
@@ -4013,6 +4300,7 @@ int spi_nor_scan(struct spi_nor *nor)
struct spi_slave *spi = nor->spi;
int ret;
int cfi_mtd_nb = 0;
+ bool shift = 0;
#ifdef CONFIG_FLASH_CFI_MTD
cfi_mtd_nb = CFI_FLASH_BANKS;
@@ -4150,7 +4438,9 @@ int spi_nor_scan(struct spi_nor *nor)
nor->addr_width = 3;
}
- if (nor->addr_width == 3 && mtd->size > SZ_16M) {
+ if (nor->flags & (SNOR_F_HAS_PARALLEL | SNOR_F_HAS_STACKED))
+ shift = 1;
+ if (nor->addr_width == 3 && (mtd->size >> shift) > SZ_16M) {
#ifndef CONFIG_SPI_FLASH_BAR
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
@@ -4160,6 +4450,7 @@ int spi_nor_scan(struct spi_nor *nor)
#else
/* Configure the BAR - discover bank cmds and read current bank */
nor->addr_width = 3;
+ set_4byte(nor, info, 0);
ret = read_bar(nor, info);
if (ret < 0)
return ret;
@@ -4177,6 +4468,14 @@ int spi_nor_scan(struct spi_nor *nor)
if (ret)
return ret;
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ ret = spi_nor_init(nor);
+ if (ret)
+ return ret;
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+
nor->rdsr_dummy = params.rdsr_dummy;
nor->rdsr_addr_nbytes = params.rdsr_addr_nbytes;
nor->name = info->name;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 35030ab..238e622 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -50,6 +50,13 @@ config SPI_DIRMAP
improvements as it automates the whole process of sending SPI memory
operations every time a new region is accessed.
+config SPI_ADVANCE
+ bool "Enable the advance feature"
+ default y
+ help
+ Enable the SPI advance feature support. By default this is set to y.
+ If you intend not to use the advance feature support you should say n here.
+
if DM_SPI
config ALTERA_SPI
diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c
index 8e227d1..dafaf11 100644
--- a/drivers/spi/altera_spi.c
+++ b/drivers/spi/altera_spi.c
@@ -95,7 +95,7 @@ static int altera_spi_xfer(struct udevice *dev, unsigned int bitlen,
uint32_t reg, data, start;
debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
- dev_seq(bus), slave_plat->cs, bitlen, bytes, flags);
+ dev_seq(bus), slave_plat->cs[0], bitlen, bytes, flags);
if (bitlen == 0)
goto done;
@@ -110,7 +110,7 @@ static int altera_spi_xfer(struct udevice *dev, unsigned int bitlen,
readl(&regs->rxdata);
if (flags & SPI_XFER_BEGIN)
- spi_cs_activate(dev, slave_plat->cs);
+ spi_cs_activate(dev, slave_plat->cs[0]);
while (bytes--) {
if (txp)
diff --git a/drivers/spi/atcspi200_spi.c b/drivers/spi/atcspi200_spi.c
index 929bf90..100d668 100644
--- a/drivers/spi/atcspi200_spi.c
+++ b/drivers/spi/atcspi200_spi.c
@@ -320,7 +320,7 @@ static int atcspi200_spi_claim_bus(struct udevice *dev)
struct udevice *bus = dev->parent;
struct nds_spi_slave *ns = dev_get_priv(bus);
- if (slave_plat->cs >= ns->num_cs) {
+ if (slave_plat->cs[0] >= ns->num_cs) {
printf("Invalid SPI chipselect\n");
return -EINVAL;
}
diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c
index faefac7..965a844 100644
--- a/drivers/spi/ath79_spi.c
+++ b/drivers/spi/ath79_spi.c
@@ -73,7 +73,7 @@ static int ath79_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (restbits)
bytes++;
- out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs));
+ out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs[0]));
while (bytes > 0) {
bytes--;
curbyte = 0;
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 79f0100..aaf3edd 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -125,7 +125,7 @@ static int atmel_spi_claim_bus(struct udevice *dev)
struct atmel_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
struct at91_spi *reg_base = bus_plat->regs;
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
u32 freq = priv->freq;
u32 scbr, csrx, mode;
@@ -174,7 +174,7 @@ static void atmel_spi_cs_activate(struct udevice *dev)
struct udevice *bus = dev_get_parent(dev);
struct atmel_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
if (!dm_gpio_is_valid(&priv->cs_gpios[cs]))
return;
@@ -189,7 +189,7 @@ static void atmel_spi_cs_deactivate(struct udevice *dev)
struct udevice *bus = dev_get_parent(dev);
struct atmel_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
if (!dm_gpio_is_valid(&priv->cs_gpios[cs]))
return;
diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c
index 1aa43fd..e9f0b34 100644
--- a/drivers/spi/bcm63xx_hsspi.c
+++ b/drivers/spi/bcm63xx_hsspi.c
@@ -174,7 +174,7 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
set = DIV_ROUND_UP(2048, set);
set &= SPI_PFL_CLK_FREQ_MASK;
set |= SPI_PFL_CLK_RSTLOOP_MASK;
- writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs));
+ writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs[0]));
/* profile signal */
set = 0;
@@ -192,29 +192,29 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
if (speed > SPI_MAX_SYNC_CLOCK)
set |= SPI_PFL_SIG_ASYNCIN_MASK;
- clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
+ clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs[0]), clr, set);
/* global control */
set = 0;
clr = 0;
if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
- if (priv->cs_pols & BIT(plat->cs))
- set |= BIT(plat->cs);
+ if (priv->cs_pols & BIT(plat->cs[0]))
+ set |= BIT(plat->cs[0]);
else
- clr |= BIT(plat->cs);
+ clr |= BIT(plat->cs[0]);
} else {
/* invert cs polarity */
- if (priv->cs_pols & BIT(plat->cs))
- clr |= BIT(plat->cs);
+ if (priv->cs_pols & BIT(plat->cs[0]))
+ clr |= BIT(plat->cs[0]);
else
- set |= BIT(plat->cs);
+ set |= BIT(plat->cs[0]);
/* invert dummy cs polarity */
- if (priv->cs_pols & BIT(!plat->cs))
- clr |= BIT(!plat->cs);
+ if (priv->cs_pols & BIT(!plat->cs[0]))
+ clr |= BIT(!plat->cs[0]);
else
- set |= BIT(!plat->cs);
+ set |= BIT(!plat->cs[0]);
}
clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set);
@@ -290,7 +290,7 @@ static int bcm63xx_hsspi_xfer_dummy_cs(struct udevice *dev, unsigned int data_by
if (plat->mode & SPI_3WIRE)
val |= SPI_PFL_MODE_3WIRE_MASK;
- writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
+ writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs[0]));
/* transfer loop */
while (data_bytes > 0) {
@@ -310,9 +310,9 @@ static int bcm63xx_hsspi_xfer_dummy_cs(struct udevice *dev, unsigned int data_by
/* issue the transfer */
val = SPI_CMD_OP_START;
- val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
+ val |= (plat->cs[0] << SPI_CMD_PFL_SHIFT) &
SPI_CMD_PFL_MASK;
- val |= (!plat->cs << SPI_CMD_SLAVE_SHIFT) &
+ val |= (!plat->cs[0] << SPI_CMD_SLAVE_SHIFT) &
SPI_CMD_SLAVE_MASK;
writel(val, priv->regs + SPI_CMD_REG);
@@ -450,7 +450,7 @@ static int bcm63xx_hsspi_xfer_prepend(struct udevice *dev, unsigned int data_byt
}
}
val |= (priv->prepend_cnt << SPI_PFL_MODE_PREPCNT_SHIFT);
- writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
+ writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs[0]));
/* set fifo operation */
val = opcode | (data_bytes & HSSPI_FIFO_OP_BYTES_MASK);
@@ -459,9 +459,9 @@ static int bcm63xx_hsspi_xfer_prepend(struct udevice *dev, unsigned int data_byt
/* issue the transfer */
val = SPI_CMD_OP_START;
- val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
+ val |= (plat->cs[0] << SPI_CMD_PFL_SHIFT) &
SPI_CMD_PFL_MASK;
- val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) &
+ val |= (plat->cs[0] << SPI_CMD_SLAVE_SHIFT) &
SPI_CMD_SLAVE_MASK;
writel(val, priv->regs + SPI_CMD_REG);
@@ -537,16 +537,16 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev)
struct spi_slave *slave = dev_get_parent_priv(dev);
/* check cs */
- if (plat->cs >= priv->num_cs) {
- printf("no cs %u\n", plat->cs);
+ if (plat->cs[0] >= priv->num_cs) {
+ printf("no cs %u\n", plat->cs[0]);
return -ENODEV;
}
/* cs polarity */
if (plat->mode & SPI_CS_HIGH)
- priv->cs_pols |= BIT(plat->cs);
+ priv->cs_pols |= BIT(plat->cs[0]);
else
- priv->cs_pols &= ~BIT(plat->cs);
+ priv->cs_pols &= ~BIT(plat->cs[0]);
/*
* set the max read/write size to make sure each xfer are within the
diff --git a/drivers/spi/bcm63xx_spi.c b/drivers/spi/bcm63xx_spi.c
index 595b41c..e02ec7e 100644
--- a/drivers/spi/bcm63xx_spi.c
+++ b/drivers/spi/bcm63xx_spi.c
@@ -275,7 +275,7 @@ static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* issue the transfer */
cmd = SPI_CMD_OP_START;
- cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
+ cmd |= (plat->cs[0] << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK;
cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT);
if (plat->mode & SPI_3WIRE)
cmd |= SPI_CMD_3WIRE_MASK;
@@ -353,8 +353,8 @@ static int bcm63xx_spi_child_pre_probe(struct udevice *dev)
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
/* check cs */
- if (plat->cs >= priv->num_cs) {
- printf("no cs %u\n", plat->cs);
+ if (plat->cs[0] >= priv->num_cs) {
+ printf("no cs %u\n", plat->cs[0]);
return -ENODEV;
}
diff --git a/drivers/spi/bcmbca_hsspi.c b/drivers/spi/bcmbca_hsspi.c
index eff9e11..209ca71 100644
--- a/drivers/spi/bcmbca_hsspi.c
+++ b/drivers/spi/bcmbca_hsspi.c
@@ -155,7 +155,7 @@ static void bcmbca_hsspi_setup_clock(struct bcmbca_hsspi_priv *priv,
set = DIV_ROUND_UP(2048, set);
set &= SPI_PFL_CLK_FREQ_MASK;
set |= SPI_PFL_CLK_RSTLOOP_MASK;
- writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs));
+ writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs[0]));
/* profile signal */
set = 0;
@@ -173,16 +173,16 @@ static void bcmbca_hsspi_setup_clock(struct bcmbca_hsspi_priv *priv,
if (priv->speed > SPI_MAX_SYNC_CLOCK)
set |= SPI_PFL_SIG_ASYNCIN_MASK;
- clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
+ clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs[0]), clr, set);
/* global control */
set = 0;
clr = 0;
- if (priv->cs_pols & BIT(plat->cs))
- set |= BIT(plat->cs);
+ if (priv->cs_pols & BIT(plat->cs[0]))
+ set |= BIT(plat->cs[0]);
else
- clr |= BIT(plat->cs);
+ clr |= BIT(plat->cs[0]);
clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set);
}
@@ -194,7 +194,7 @@ static void bcmbca_hsspi_activate_cs(struct bcmbca_hsspi_priv *priv,
/* set the override bit */
val = readl(priv->spim_ctrl);
- val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
+ val |= BIT(plat->cs[0] + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
writel(val, priv->spim_ctrl);
}
@@ -205,7 +205,7 @@ static void bcmbca_hsspi_deactivate_cs(struct bcmbca_hsspi_priv *priv,
/* clear the cs override bit */
val = readl(priv->spim_ctrl);
- val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
+ val &= ~BIT(plat->cs[0] + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
writel(val, priv->spim_ctrl);
}
@@ -250,7 +250,7 @@ static int bcmbca_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
if (plat->mode & SPI_3WIRE)
val |= SPI_PFL_MODE_3WIRE_MASK;
- writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
+ writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs[0]));
/* transfer loop */
while (data_bytes > 0) {
@@ -276,9 +276,9 @@ static int bcmbca_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
/* issue the transfer */
val = SPI_CMD_OP_START;
- val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
+ val |= (plat->cs[0] << SPI_CMD_PFL_SHIFT) &
SPI_CMD_PFL_MASK;
- val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) &
+ val |= (plat->cs[0] << SPI_CMD_SLAVE_SHIFT) &
SPI_CMD_SLAVE_MASK;
writel(val, priv->regs + SPI_CMD_REG);
@@ -326,22 +326,22 @@ static int bcmbca_hsspi_child_pre_probe(struct udevice *dev)
u32 val;
/* check cs */
- if (plat->cs >= priv->num_cs) {
- dev_err(dev, "no cs %u\n", plat->cs);
+ if (plat->cs[0] >= priv->num_cs) {
+ dev_err(dev, "no cs %u\n", plat->cs[0]);
return -EINVAL;
}
/* cs polarity */
if (plat->mode & SPI_CS_HIGH)
- priv->cs_pols |= BIT(plat->cs);
+ priv->cs_pols |= BIT(plat->cs[0]);
else
- priv->cs_pols &= ~BIT(plat->cs);
+ priv->cs_pols &= ~BIT(plat->cs[0]);
/* set the polarity to spim cs register */
val = readl(priv->spim_ctrl);
- val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
- if (priv->cs_pols & BIT(plat->cs))
- val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
+ val &= ~BIT(plat->cs[0] + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
+ if (priv->cs_pols & BIT(plat->cs[0]))
+ val |= BIT(plat->cs[0] + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
writel(val, priv->spim_ctrl);
return 0;
diff --git a/drivers/spi/cf_spi.c b/drivers/spi/cf_spi.c
index 8234468..84077c0 100644
--- a/drivers/spi/cf_spi.c
+++ b/drivers/spi/cf_spi.c
@@ -123,7 +123,7 @@ static int coldfire_spi_claim_bus(struct udevice *dev)
/* Clear FIFO and resume transfer */
clrbits_be32(&dspi->mcr, DSPI_MCR_CTXF | DSPI_MCR_CRXF);
- dspi_chip_select(slave_plat->cs);
+ dspi_chip_select(slave_plat->cs[0]);
return 0;
}
@@ -139,7 +139,7 @@ static int coldfire_spi_release_bus(struct udevice *dev)
/* Clear FIFO */
clrbits_be32(&dspi->mcr, DSPI_MCR_CTXF | DSPI_MCR_CRXF);
- dspi_chip_unselect(slave_plat->cs);
+ dspi_chip_unselect(slave_plat->cs[0]);
return 0;
}
@@ -168,7 +168,7 @@ static int coldfire_spi_xfer(struct udevice *dev, unsigned int bitlen,
if ((flags & SPI_XFER_BEGIN) == SPI_XFER_BEGIN)
ctrl |= DSPI_TFR_CONT;
- ctrl = setup_ctrl(ctrl, slave_plat->cs);
+ ctrl = setup_ctrl(ctrl, slave_plat->cs[0]);
if (len > 1) {
int tmp_len = len - 1;
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c
index 04c134b..e5c1373 100644
--- a/drivers/spi/davinci_spi.c
+++ b/drivers/spi/davinci_spi.c
@@ -339,13 +339,13 @@ static int davinci_spi_claim_bus(struct udevice *dev)
struct udevice *bus = dev->parent;
struct davinci_spi_slave *ds = dev_get_priv(bus);
- if (slave_plat->cs >= ds->num_cs) {
+ if (slave_plat->cs[0] >= ds->num_cs) {
printf("Invalid SPI chipselect\n");
return -EINVAL;
}
ds->half_duplex = slave_plat->mode & SPI_PREAMBLE;
- return __davinci_spi_claim_bus(ds, slave_plat->cs);
+ return __davinci_spi_claim_bus(ds, slave_plat->cs[0]);
}
static int davinci_spi_release_bus(struct udevice *dev)
@@ -364,11 +364,11 @@ static int davinci_spi_xfer(struct udevice *dev, unsigned int bitlen,
struct udevice *bus = dev->parent;
struct davinci_spi_slave *ds = dev_get_priv(bus);
- if (slave->cs >= ds->num_cs) {
+ if (slave->cs[0] >= ds->num_cs) {
printf("Invalid SPI chipselect\n");
return -EINVAL;
}
- ds->cur_cs = slave->cs;
+ ds->cur_cs = slave->cs[0];
return __davinci_spi_xfer(ds, bitlen, dout, din, flags);
}
diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c
index 1d4d90c..f2393c0 100644
--- a/drivers/spi/fsl_dspi.c
+++ b/drivers/spi/fsl_dspi.c
@@ -452,9 +452,9 @@ static int fsl_dspi_child_pre_probe(struct udevice *dev)
unsigned char pcssck = 0, cssck = 0;
unsigned char pasc = 0, asc = 0;
- if (slave_plat->cs >= priv->num_chipselect) {
+ if (slave_plat->cs[0] >= priv->num_chipselect) {
debug("DSPI invalid chipselect number %d(max %d)!\n",
- slave_plat->cs, priv->num_chipselect - 1);
+ slave_plat->cs[0], priv->num_chipselect - 1);
return -EINVAL;
}
@@ -469,12 +469,12 @@ static int fsl_dspi_child_pre_probe(struct udevice *dev)
/* Set After SCK delay scale values */
ns_delay_scale(&pasc, &asc, sck_cs_delay, priv->bus_clk);
- priv->ctar_val[slave_plat->cs] = DSPI_CTAR_DEFAULT_VALUE |
+ priv->ctar_val[slave_plat->cs[0]] = DSPI_CTAR_DEFAULT_VALUE |
DSPI_CTAR_PCSSCK(pcssck) |
DSPI_CTAR_PASC(pasc);
debug("DSPI pre_probe slave device on CS %u, max_hz %u, mode 0x%x.\n",
- slave_plat->cs, slave_plat->max_hz, slave_plat->mode);
+ slave_plat->cs[0], slave_plat->max_hz, slave_plat->mode);
return 0;
}
@@ -527,13 +527,13 @@ static int fsl_dspi_claim_bus(struct udevice *dev)
priv = dev_get_priv(bus);
/* processor special preparation work */
- cpu_dspi_claim_bus(dev_seq(bus), slave_plat->cs);
+ cpu_dspi_claim_bus(dev_seq(bus), slave_plat->cs[0]);
/* configure transfer mode */
- fsl_dspi_cfg_ctar_mode(priv, slave_plat->cs, priv->mode);
+ fsl_dspi_cfg_ctar_mode(priv, slave_plat->cs[0], priv->mode);
/* configure active state of CSX */
- fsl_dspi_cfg_cs_active_state(priv, slave_plat->cs,
+ fsl_dspi_cfg_cs_active_state(priv, slave_plat->cs[0],
priv->mode);
fsl_dspi_clr_fifo(priv);
@@ -559,7 +559,7 @@ static int fsl_dspi_release_bus(struct udevice *dev)
dspi_halt(priv, 1);
/* processor special release work */
- cpu_dspi_release_bus(dev_seq(bus), slave_plat->cs);
+ cpu_dspi_release_bus(dev_seq(bus), slave_plat->cs[0]);
return 0;
}
@@ -615,7 +615,7 @@ static int fsl_dspi_xfer(struct udevice *dev, unsigned int bitlen,
bus = dev->parent;
priv = dev_get_priv(bus);
- return dspi_xfer(priv, slave_plat->cs, bitlen, dout, din, flags);
+ return dspi_xfer(priv, slave_plat->cs[0], bitlen, dout, din, flags);
}
static int fsl_dspi_set_speed(struct udevice *bus, uint speed)
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index 2638ed2..7ed35aa 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -513,8 +513,8 @@ static int fsl_espi_child_pre_probe(struct udevice *dev)
struct udevice *bus = dev->parent;
struct fsl_spi_slave *fsl = dev_get_priv(bus);
- debug("%s cs %u\n", __func__, slave_plat->cs);
- fsl->cs = slave_plat->cs;
+ debug("%s cs %u\n", __func__, slave_plat->cs[0]);
+ fsl->cs = slave_plat->cs[0];
return 0;
}
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c
index 8a0a53c..c7f5548 100644
--- a/drivers/spi/fsl_qspi.c
+++ b/drivers/spi/fsl_qspi.c
@@ -510,10 +510,10 @@ static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_slave *slave)
struct dm_spi_slave_plat *plat =
dev_get_parent_plat(slave->dev);
- if (q->selected == plat->cs)
+ if (q->selected == plat->cs[0])
return;
- q->selected = plat->cs;
+ q->selected = plat->cs[0];
fsl_qspi_invalidate(q);
}
diff --git a/drivers/spi/gxp_spi.c b/drivers/spi/gxp_spi.c
index 70d76ac..3ee369c 100644
--- a/drivers/spi/gxp_spi.c
+++ b/drivers/spi/gxp_spi.c
@@ -87,7 +87,7 @@ static int gxp_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *do
value = readl(priv->base + OFFSET_SPIMCFG);
value &= ~(1 << 24);
/* set chipselect */
- value |= (slave_plat->cs << 24);
+ value |= (slave_plat->cs[0] << 24);
/* addr reg and addr size */
if (len >= 4) {
diff --git a/drivers/spi/mpc8xx_spi.c b/drivers/spi/mpc8xx_spi.c
index 7e72fb9..51cc487 100644
--- a/drivers/spi/mpc8xx_spi.c
+++ b/drivers/spi/mpc8xx_spi.c
@@ -148,7 +148,7 @@ static void mpc8xx_spi_cs_activate(struct udevice *dev)
struct mpc8xx_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *platdata = dev_get_parent_plat(dev);
- dm_gpio_set_value(&priv->gpios[platdata->cs], 1);
+ dm_gpio_set_value(&priv->gpios[platdata->cs[0]], 1);
}
static void mpc8xx_spi_cs_deactivate(struct udevice *dev)
@@ -156,7 +156,7 @@ static void mpc8xx_spi_cs_deactivate(struct udevice *dev)
struct mpc8xx_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *platdata = dev_get_parent_plat(dev);
- dm_gpio_set_value(&priv->gpios[platdata->cs], 0);
+ dm_gpio_set_value(&priv->gpios[platdata->cs[0]], 0);
}
static int mpc8xx_spi_xfer_one(struct udevice *dev, size_t count,
diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c
index cd624f4..b34e1c2 100644
--- a/drivers/spi/mpc8xxx_spi.c
+++ b/drivers/spi/mpc8xxx_spi.c
@@ -113,7 +113,7 @@ static void mpc8xxx_spi_cs_activate(struct udevice *dev)
struct mpc8xxx_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
- dm_gpio_set_value(&priv->gpios[plat->cs], 1);
+ dm_gpio_set_value(&priv->gpios[plat->cs[0]], 1);
}
static void mpc8xxx_spi_cs_deactivate(struct udevice *dev)
@@ -121,7 +121,7 @@ static void mpc8xxx_spi_cs_deactivate(struct udevice *dev)
struct mpc8xxx_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
- dm_gpio_set_value(&priv->gpios[plat->cs], 0);
+ dm_gpio_set_value(&priv->gpios[plat->cs[0]], 0);
}
static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen,
@@ -137,10 +137,10 @@ static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen,
ulong type = dev_get_driver_data(bus);
debug("%s: slave %s:%u dout %08X din %08X bitlen %u\n", __func__,
- bus->name, plat->cs, (uint)dout, (uint)din, bitlen);
- if (plat->cs >= priv->cs_count) {
+ bus->name, plat->cs[0], (uint)dout, (uint)din, bitlen);
+ if (plat->cs[0] >= priv->cs_count) {
dev_err(dev, "chip select index %d too large (cs_count=%d)\n",
- plat->cs, priv->cs_count);
+ plat->cs[0], priv->cs_count);
return -EINVAL;
}
if (bitlen % 8) {
diff --git a/drivers/spi/mscc_bb_spi.c b/drivers/spi/mscc_bb_spi.c
index ad4daeb..75ab4ab 100644
--- a/drivers/spi/mscc_bb_spi.c
+++ b/drivers/spi/mscc_bb_spi.c
@@ -123,11 +123,11 @@ int mscc_bb_spi_xfer(struct udevice *dev, unsigned int bitlen,
u8 *rxd = din;
debug("spi_xfer: slave %s:%s cs%d mode %d, dout %p din %p bitlen %u\n",
- dev->parent->name, dev->name, plat->cs, plat->mode, dout,
+ dev->parent->name, dev->name, plat->cs[0], plat->mode, dout,
din, bitlen);
if (flags & SPI_XFER_BEGIN)
- mscc_bb_spi_cs_activate(priv, plat->mode, plat->cs);
+ mscc_bb_spi_cs_activate(priv, plat->mode, plat->cs[0]);
count = bitlen / 8;
for (i = 0; i < count; i++) {
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index ff61a14..b864a69 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -135,7 +135,7 @@ static void mxc_spi_cs_activate(struct mxc_spi_slave *mxcs)
struct udevice *dev = mxcs->dev;
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
if (!dm_gpio_is_valid(&mxcs->cs_gpios[cs]))
return;
@@ -153,7 +153,7 @@ static void mxc_spi_cs_deactivate(struct mxc_spi_slave *mxcs)
struct udevice *dev = mxcs->dev;
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
if (!dm_gpio_is_valid(&mxcs->cs_gpios[cs]))
return;
@@ -633,7 +633,7 @@ static int mxc_spi_claim_bus(struct udevice *dev)
mxcs->dev = dev;
- return mxc_spi_claim_bus_internal(mxcs, slave_plat->cs);
+ return mxc_spi_claim_bus_internal(mxcs, slave_plat->cs[0]);
}
static int mxc_spi_release_bus(struct udevice *dev)
diff --git a/drivers/spi/npcm_fiu_spi.c b/drivers/spi/npcm_fiu_spi.c
index 73c5064..7b8271c 100644
--- a/drivers/spi/npcm_fiu_spi.c
+++ b/drivers/spi/npcm_fiu_spi.c
@@ -203,7 +203,7 @@ static int npcm_fiu_spi_xfer(struct udevice *dev, unsigned int bitlen,
int len;
if (flags & SPI_XFER_BEGIN)
- activate_cs(regs, slave_plat->cs);
+ activate_cs(regs, slave_plat->cs[0]);
while (bytes) {
len = (bytes > CHUNK_SIZE) ? CHUNK_SIZE : bytes;
@@ -222,7 +222,7 @@ static int npcm_fiu_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
if (flags & SPI_XFER_END)
- deactivate_cs(regs, slave_plat->cs);
+ deactivate_cs(regs, slave_plat->cs[0]);
return ret;
}
@@ -325,9 +325,9 @@ static int npcm_fiu_exec_op(struct spi_slave *slave,
bytes = op->data.nbytes;
addr = (u32)op->addr.val;
if (!bytes) {
- activate_cs(regs, slave_plat->cs);
+ activate_cs(regs, slave_plat->cs[0]);
ret = npcm_fiu_uma_operation(priv, op, addr, NULL, NULL, 0, false);
- deactivate_cs(regs, slave_plat->cs);
+ deactivate_cs(regs, slave_plat->cs[0]);
return ret;
}
@@ -339,9 +339,9 @@ static int npcm_fiu_exec_op(struct spi_slave *slave,
* Use HW-control CS for read to avoid clock and timing issues.
*/
if (op->data.dir == SPI_MEM_DATA_OUT)
- activate_cs(regs, slave_plat->cs);
+ activate_cs(regs, slave_plat->cs[0]);
else
- writel(FIELD_PREP(UMA_CTS_DEV_NUM_MASK, slave_plat->cs) | UMA_CTS_SW_CS,
+ writel(FIELD_PREP(UMA_CTS_DEV_NUM_MASK, slave_plat->cs[0]) | UMA_CTS_SW_CS,
&regs->uma_cts);
while (bytes) {
len = (bytes > CHUNK_SIZE) ? CHUNK_SIZE : bytes;
@@ -361,7 +361,7 @@ static int npcm_fiu_exec_op(struct spi_slave *slave,
rx += len;
}
if (op->data.dir == SPI_MEM_DATA_OUT)
- deactivate_cs(regs, slave_plat->cs);
+ deactivate_cs(regs, slave_plat->cs[0]);
return 0;
}
diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c
index fefdaaa..7489c89 100644
--- a/drivers/spi/nxp_fspi.c
+++ b/drivers/spi/nxp_fspi.c
@@ -962,7 +962,7 @@ static int nxp_fspi_claim_bus(struct udevice *dev)
bus = dev->parent;
f = dev_get_priv(bus);
- nxp_fspi_select_mem(f, slave_plat->cs);
+ nxp_fspi_select_mem(f, slave_plat->cs[0]);
return 0;
}
diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
index 4bc38be..0e6e0f7 100644
--- a/drivers/spi/octeon_spi.c
+++ b/drivers/spi/octeon_spi.c
@@ -93,7 +93,7 @@ static u64 octeon_spi_set_mpicfg(struct udevice *dev)
if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
- debug("\n slave params %d %d %d\n", slave->cs,
+ debug("\n slave params %d %d %d\n", slave->cs[0],
slave->max_hz, slave->mode);
cpha = !!(slave->mode & SPI_CPHA);
cpol = !!(slave->mode & SPI_CPOL);
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 3d82fc7..35bd876 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -393,7 +393,7 @@ static int omap3_spi_claim_bus(struct udevice *dev)
struct omap3_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- priv->cs = slave_plat->cs;
+ priv->cs = slave_plat->cs[0];
if (!priv->freq)
priv->freq = slave_plat->max_hz;
@@ -422,7 +422,7 @@ static int omap3_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
struct omap3_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- priv->cs = slave_plat->cs;
+ priv->cs = slave_plat->cs[0];
priv->wordlen = wordlen;
_omap3_spi_set_wordlen(priv);
diff --git a/drivers/spi/pic32_spi.c b/drivers/spi/pic32_spi.c
index e11ae7f..c4b31dc 100644
--- a/drivers/spi/pic32_spi.c
+++ b/drivers/spi/pic32_spi.c
@@ -247,7 +247,7 @@ static int pic32_spi_xfer(struct udevice *slave, unsigned int bitlen,
slave_plat = dev_get_parent_plat(slave);
debug("spi_xfer: bus:%i cs:%i flags:%lx\n",
- dev_seq(bus), slave_plat->cs, flags);
+ dev_seq(bus), slave_plat->cs[0], flags);
debug("msg tx %p, rx %p submitted of %d byte(s)\n",
tx_buf, rx_buf, len);
diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c
index 4571dc9..2c3d70b 100644
--- a/drivers/spi/rk_spi.c
+++ b/drivers/spi/rk_spi.c
@@ -444,7 +444,7 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Assert CS before transfer */
if (flags & SPI_XFER_BEGIN)
- spi_cs_activate(dev, slave_plat->cs);
+ spi_cs_activate(dev, slave_plat->cs[0]);
/*
* To ensure fast loading of firmware images (e.g. full U-Boot
@@ -507,7 +507,7 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Deassert CS after transfer */
if (flags & SPI_XFER_END)
- spi_cs_deactivate(dev, slave_plat->cs);
+ spi_cs_deactivate(dev, slave_plat->cs[0]);
rkspi_enable_chip(regs, false);
if (!out)
diff --git a/drivers/spi/rockchip_sfc.c b/drivers/spi/rockchip_sfc.c
index 596c22a..71e9b70 100644
--- a/drivers/spi/rockchip_sfc.c
+++ b/drivers/spi/rockchip_sfc.c
@@ -409,7 +409,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
/* set the Controller */
ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE;
- cmd |= plat->cs << SFC_CMD_CS_SHIFT;
+ cmd |= plat->cs[0] << SFC_CMD_CS_SHIFT;
dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n",
op->addr.nbytes, op->addr.buswidth,
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index d91d58d..4b468da 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -192,7 +192,7 @@ static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz)
if (found) {
hclk_div = hclk_masks[i] << 8;
- priv->flashes[slave_plat->cs].max_freq = hclk_clk / (i + 1);
+ priv->flashes[slave_plat->cs[0]].max_freq = hclk_clk / (i + 1);
}
dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
@@ -200,7 +200,7 @@ static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz)
if (found) {
dev_dbg(dev, "h_div: %d (mask %x), speed: %d\n",
- i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+ i + 1, hclk_masks[i], priv->flashes[slave_plat->cs[0]].max_freq);
}
return hclk_div;
@@ -311,7 +311,7 @@ static u32 ast2500_get_clk_setting(struct udevice *dev, uint max_hz)
for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
if (hclk_clk / (i + 1) <= max_hz) {
found = true;
- priv->flashes[slave_plat->cs].max_freq =
+ priv->flashes[slave_plat->cs[0]].max_freq =
hclk_clk / (i + 1);
break;
}
@@ -325,7 +325,7 @@ static u32 ast2500_get_clk_setting(struct udevice *dev, uint max_hz)
for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
if (hclk_clk / ((i + 1) * 4) <= max_hz) {
found = true;
- priv->flashes[slave_plat->cs].max_freq =
+ priv->flashes[slave_plat->cs[0]].max_freq =
hclk_clk / ((i + 1) * 4);
break;
}
@@ -340,7 +340,7 @@ end:
if (found) {
dev_dbg(dev, "h_div: %d (mask %x), speed: %d\n",
- i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+ i + 1, hclk_masks[i], priv->flashes[slave_plat->cs[0]].max_freq);
}
return hclk_div;
@@ -456,7 +456,7 @@ static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz)
if (found) {
hclk_div = ((j << 24) | hclk_masks[i] << 8);
- priv->flashes[slave_plat->cs].max_freq =
+ priv->flashes[slave_plat->cs[0]].max_freq =
hclk_clk / (i + 1 + j * 16);
break;
}
@@ -467,7 +467,7 @@ static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz)
if (found) {
dev_dbg(dev, "base_clk: %d, h_div: %d (mask %x), speed: %d\n",
- j, i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+ j, i + 1, hclk_masks[i], priv->flashes[slave_plat->cs[0]].max_freq);
}
return hclk_div;
@@ -588,7 +588,7 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
struct udevice *bus = dev->parent;
struct aspeed_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
u32 ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
u32 ce_ctrl_val;
struct aspeed_spi_flash *flash = &priv->flashes[cs];
@@ -668,7 +668,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
const struct aspeed_spi_info *info = priv->info;
struct spi_mem_op op_tmpl = desc->info.op_tmpl;
u32 i;
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
u32 cmd_io_conf;
u32 ce_ctrl_reg;
@@ -725,7 +725,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
struct udevice *dev = desc->slave->dev;
struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
int ret;
dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%x\n",
@@ -750,7 +750,7 @@ static struct aspeed_spi_flash *aspeed_spi_get_flash(struct udevice *dev)
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
struct aspeed_spi_plat *plat = dev_get_plat(bus);
struct aspeed_spi_priv *priv = dev_get_priv(bus);
- u32 cs = slave_plat->cs;
+ u32 cs = slave_plat->cs[0];
if (cs >= plat->max_cs) {
dev_err(dev, "invalid CS %u\n", cs);
@@ -1069,10 +1069,10 @@ static int aspeed_spi_claim_bus(struct udevice *dev)
struct udevice *bus = dev->parent;
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
- struct aspeed_spi_flash *flash = &priv->flashes[slave_plat->cs];
+ struct aspeed_spi_flash *flash = &priv->flashes[slave_plat->cs[0]];
u32 clk_setting;
- dev_dbg(bus, "%s: claim bus CS%u\n", bus->name, slave_plat->cs);
+ dev_dbg(bus, "%s: claim bus CS%u\n", bus->name, slave_plat->cs[0]);
if (flash->max_freq == 0) {
clk_setting = priv->info->get_clk_setting(dev, slave_plat->max_hz);
@@ -1090,7 +1090,7 @@ static int aspeed_spi_release_bus(struct udevice *dev)
struct udevice *bus = dev->parent;
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- dev_dbg(bus, "%s: release bus CS%u\n", bus->name, slave_plat->cs);
+ dev_dbg(bus, "%s: release bus CS%u\n", bus->name, slave_plat->cs[0]);
if (!aspeed_spi_get_flash(dev))
return -ENODEV;
diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c
index b98bcd9..3835865 100644
--- a/drivers/spi/spi-mxic.c
+++ b/drivers/spi/spi-mxic.c
@@ -366,8 +366,8 @@ static int mxic_spi_mem_exec_op(struct spi_slave *slave,
nio = 2;
writel(HC_CFG_NIO(nio) |
- HC_CFG_TYPE(slave_plat->cs, HC_CFG_TYPE_SPI_NOR) |
- HC_CFG_SLV_ACT(slave_plat->cs) | HC_CFG_IDLE_SIO_LVL(1) |
+ HC_CFG_TYPE(slave_plat->cs[0], HC_CFG_TYPE_SPI_NOR) |
+ HC_CFG_SLV_ACT(slave_plat->cs[0]) | HC_CFG_IDLE_SIO_LVL(1) |
HC_CFG_MAN_CS_EN,
priv->regs + HC_CFG);
writel(HC_EN_BIT, priv->regs + HC_EN);
@@ -396,7 +396,7 @@ static int mxic_spi_mem_exec_op(struct spi_slave *slave,
ss_ctrl |= OP_READ;
}
- writel(ss_ctrl, priv->regs + SS_CTRL(slave_plat->cs));
+ writel(ss_ctrl, priv->regs + SS_CTRL(slave_plat->cs[0]));
writel(readl(priv->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
priv->regs + HC_CFG);
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 836c550..dc001e6 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -718,7 +718,7 @@ static int qup_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (ret != 0)
return ret;
- ret = qup_spi_set_cs(bus, slave_plat->cs, false);
+ ret = qup_spi_set_cs(bus, slave_plat->cs[0], false);
if (ret != 0)
return ret;
}
@@ -736,7 +736,7 @@ static int qup_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
if (flags & SPI_XFER_END) {
- ret = qup_spi_set_cs(bus, slave_plat->cs, true);
+ ret = qup_spi_set_cs(bus, slave_plat->cs[0], true);
if (ret != 0)
return ret;
}
diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
index 0c8666c..15407d4 100644
--- a/drivers/spi/spi-sifive.c
+++ b/drivers/spi/spi-sifive.c
@@ -108,13 +108,13 @@ static void sifive_spi_prep_device(struct sifive_spi *spi,
{
/* Update the chip select polarity */
if (slave_plat->mode & SPI_CS_HIGH)
- spi->cs_inactive &= ~BIT(slave_plat->cs);
+ spi->cs_inactive &= ~BIT(slave_plat->cs[0]);
else
- spi->cs_inactive |= BIT(slave_plat->cs);
+ spi->cs_inactive |= BIT(slave_plat->cs[0]);
writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
/* Select the correct device */
- writel(slave_plat->cs, spi->regs + SIFIVE_SPI_REG_CSID);
+ writel(slave_plat->cs[0], spi->regs + SIFIVE_SPI_REG_CSID);
}
static int sifive_spi_set_cs(struct sifive_spi *spi,
diff --git a/drivers/spi/spi-sn-f-ospi.c b/drivers/spi/spi-sn-f-ospi.c
index fc82791..364ba4b 100644
--- a/drivers/spi/spi-sn-f-ospi.c
+++ b/drivers/spi/spi-sn-f-ospi.c
@@ -497,7 +497,7 @@ static int f_ospi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
int err = 0;
slave_plat = dev_get_parent_plat(slave->dev);
- ospi->chip_select = slave_plat->cs;
+ ospi->chip_select = slave_plat->cs[0];
switch (op->data.dir) {
case SPI_MEM_DATA_IN:
diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c
index 13725ee..9be4a1e 100644
--- a/drivers/spi/spi-sunxi.c
+++ b/drivers/spi/spi-sunxi.c
@@ -352,7 +352,7 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
if (flags & SPI_XFER_BEGIN)
- sun4i_spi_set_cs(bus, slave_plat->cs, true);
+ sun4i_spi_set_cs(bus, slave_plat->cs[0], true);
/* Reset FIFOs */
setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) |
@@ -383,7 +383,7 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
false, SUN4I_SPI_TIMEOUT_MS, false);
if (ret < 0) {
printf("ERROR: sun4i_spi: Timeout transferring data\n");
- sun4i_spi_set_cs(bus, slave_plat->cs, false);
+ sun4i_spi_set_cs(bus, slave_plat->cs[0], false);
return ret;
}
@@ -394,7 +394,7 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
if (flags & SPI_XFER_END)
- sun4i_spi_set_cs(bus, slave_plat->cs, false);
+ sun4i_spi_set_cs(bus, slave_plat->cs[0], false);
return 0;
}
diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c
index eb522fd..a3c0ad1 100644
--- a/drivers/spi/spi-synquacer.c
+++ b/drivers/spi/spi-synquacer.c
@@ -193,12 +193,12 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
/* if nothing to do */
if (slave_plat->mode == priv->mode &&
rwflag == priv->rwflag &&
- slave_plat->cs == priv->cs &&
+ slave_plat->cs[0] == priv->cs &&
slave_plat->max_hz == priv->speed)
return;
priv->rwflag = rwflag;
- priv->cs = slave_plat->cs;
+ priv->cs = slave_plat->cs[0];
priv->mode = slave_plat->mode;
priv->speed = slave_plat->max_hz;
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 6e28172..bf987ea 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -224,7 +224,7 @@ int spi_chip_select(struct udevice *dev)
{
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
- return plat ? plat->cs : -ENOENT;
+ return plat ? plat->cs[0] : -ENOENT;
}
int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp)
@@ -261,8 +261,8 @@ int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp)
struct dm_spi_slave_plat *plat;
plat = dev_get_parent_plat(dev);
- dev_dbg(bus, "%s: plat=%p, cs=%d\n", __func__, plat, plat->cs);
- if (plat->cs == cs) {
+ dev_dbg(bus, "%s: plat=%p, cs=%d\n", __func__, plat, plat->cs[0]);
+ if (plat->cs[0] == cs) {
*devp = dev;
return 0;
}
@@ -415,7 +415,7 @@ int _spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
return ret;
}
plat = dev_get_parent_plat(dev);
- plat->cs = cs;
+ plat->cs[0] = cs;
if (speed) {
plat->max_hz = speed;
} else {
@@ -446,6 +446,12 @@ int _spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
slave = dev_get_parent_priv(dev);
bus_data = dev_get_uclass_priv(bus);
+#if CONFIG_IS_ENABLED(SPI_ADVANCE)
+ if ((dev_read_bool(dev, "parallel-memories")) && !slave->multi_cs_cap) {
+ dev_err(dev, "controller doesn't support multi CS\n");
+ return -EINVAL;
+ }
+#endif
/*
* In case the operation speed is not yet established by
* dm_spi_claim_bus() ensure the bus is configured properly.
@@ -508,8 +514,26 @@ int spi_slave_of_to_plat(struct udevice *dev, struct dm_spi_slave_plat *plat)
{
int mode = 0;
int value;
+ int ret;
+
+ ret = dev_read_u32_array(dev, "reg", plat->cs, SPI_CS_CNT_MAX);
+
+ if (IS_ENABLED(CONFIG_SPL_BUILD)) {
+ if (ret == -FDT_ERR_BADLAYOUT) {
+ dev_read_u32(dev, "reg", &plat->cs[0]);
+ } else {
+ dev_err(dev, "has no valid 'reg' property (%d)\n", ret);
+ return ret;
+ }
+ } else {
+ if (ret == -EOVERFLOW) {
+ dev_read_u32(dev, "reg", &plat->cs[0]);
+ } else if (ret) {
+ dev_err(dev, "has no valid 'reg' property (%d)\n", ret);
+ return ret;
+ }
+ }
- plat->cs = dev_read_u32_default(dev, "reg", -1);
plat->max_hz = dev_read_u32_default(dev, "spi-max-frequency",
SPI_DEFAULT_SPEED_HZ);
if (dev_read_bool(dev, "spi-cpol"))
diff --git a/drivers/spi/stm32_qspi.c b/drivers/spi/stm32_qspi.c
index 2812a4d..3216ec8 100644
--- a/drivers/spi/stm32_qspi.c
+++ b/drivers/spi/stm32_qspi.c
@@ -394,7 +394,7 @@ static int stm32_qspi_claim_bus(struct udevice *dev)
{
struct stm32_qspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- int slave_cs = slave_plat->cs;
+ int slave_cs = slave_plat->cs[0];
if (slave_cs >= STM32_QSPI_MAX_CHIP)
return -ENODEV;
diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c
index 97b83b1..a1f31cf 100644
--- a/drivers/spi/stm32_spi.c
+++ b/drivers/spi/stm32_spi.c
@@ -434,7 +434,7 @@ static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
slave_plat = dev_get_parent_plat(slave);
if (flags & SPI_XFER_BEGIN)
- stm32_spi_set_cs(bus, slave_plat->cs, false);
+ stm32_spi_set_cs(bus, slave_plat->cs[0], false);
/* Be sure to have data in fifo before starting data transfer */
if (priv->tx_buf)
@@ -485,7 +485,7 @@ static int stm32_spi_xfer(struct udevice *slave, unsigned int bitlen,
stm32_spi_stopxfer(bus);
if (flags & SPI_XFER_END)
- stm32_spi_set_cs(bus, slave_plat->cs, true);
+ stm32_spi_set_cs(bus, slave_plat->cs[0], true);
return xfer_status;
}
diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c
index a16412e..1f2494e 100644
--- a/drivers/spi/ti_qspi.c
+++ b/drivers/spi/ti_qspi.c
@@ -163,7 +163,7 @@ static int ti_qspi_xfer(struct udevice *dev, unsigned int bitlen,
uchar *rxp = din;
uint status;
int timeout;
- unsigned int cs = slave->cs;
+ unsigned int cs = slave->cs[0];
bus = dev->parent;
priv = dev_get_priv(bus);
@@ -344,7 +344,7 @@ static int ti_qspi_exec_mem_op(struct spi_slave *slave,
if (from + op->data.nbytes > priv->mmap_size)
return -ENOTSUPP;
- ti_qspi_setup_mmap_read(priv, slave_plat->cs, op->cmd.opcode,
+ ti_qspi_setup_mmap_read(priv, slave_plat->cs[0], op->cmd.opcode,
op->data.buswidth, op->addr.nbytes,
op->dummy.nbytes);
@@ -363,7 +363,7 @@ static int ti_qspi_claim_bus(struct udevice *dev)
bus = dev->parent;
priv = dev_get_priv(bus);
- if (slave_plat->cs > priv->num_cs) {
+ if (slave_plat->cs[0] > priv->num_cs) {
debug("invalid qspi chip select\n");
return -EINVAL;
}
@@ -371,13 +371,13 @@ static int ti_qspi_claim_bus(struct udevice *dev)
writel(MM_SWITCH, &priv->base->memswitch);
if (priv->ctrl_mod_mmap)
ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap,
- slave_plat->cs, true);
+ slave_plat->cs[0], true);
writel(priv->dc, &priv->base->dc);
writel(0, &priv->base->cmd);
writel(0, &priv->base->data);
- priv->dc <<= slave_plat->cs * 8;
+ priv->dc <<= slave_plat->cs[0] * 8;
writel(priv->dc, &priv->base->dc);
return 0;
@@ -395,12 +395,12 @@ static int ti_qspi_release_bus(struct udevice *dev)
writel(~MM_SWITCH, &priv->base->memswitch);
if (priv->ctrl_mod_mmap)
ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap,
- slave_plat->cs, false);
+ slave_plat->cs[0], false);
writel(0, &priv->base->dc);
writel(0, &priv->base->cmd);
writel(0, &priv->base->data);
- writel(0, TI_QSPI_SETUP_REG(priv, slave_plat->cs));
+ writel(0, TI_QSPI_SETUP_REG(priv, slave_plat->cs[0]));
return 0;
}
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index 0e7fa3a..b2af17e 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -291,7 +291,7 @@ static void xilinx_spi_startup_block(struct udevice *dev)
* Perform a dummy read as a work around for
* the startup block issue.
*/
- spi_cs_activate(dev, slave_plat->cs);
+ spi_cs_activate(dev, slave_plat->cs[0]);
txp = 0x9f;
start_transfer(dev, (void *)&txp, NULL, 1);
@@ -306,7 +306,7 @@ static int xilinx_spi_xfer(struct udevice *dev, unsigned int bitlen,
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
int ret;
- spi_cs_activate(dev, slave_plat->cs);
+ spi_cs_activate(dev, slave_plat->cs[0]);
ret = start_transfer(dev, dout, din, bitlen / 8);
spi_cs_deactivate(dev);
return ret;
@@ -331,7 +331,7 @@ static int xilinx_spi_mem_exec_op(struct spi_slave *spi,
startup++;
}
- spi_cs_activate(spi->dev, slave_plat->cs);
+ spi_cs_activate(spi->dev, slave_plat->cs[0]);
if (op->cmd.opcode) {
ret = start_transfer(spi->dev, (void *)&op->cmd.opcode,
diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
index b71b9a6..281a367 100644
--- a/drivers/spi/zynq_qspi.c
+++ b/drivers/spi/zynq_qspi.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * (C) Copyright 2013 Xilinx, Inc.
+ * (C) Copyright 2013 - 2022, Xilinx, Inc.
* (C) Copyright 2015 Jagan Teki <jteki@openedev.com>
+ * (C) Copyright 2023, Advanced Micro Devices, Inc.
*
* Xilinx Zynq Quad-SPI(QSPI) controller driver (master mode only)
*/
@@ -12,10 +13,12 @@
#include <log.h>
#include <malloc.h>
#include <spi.h>
+#include <spi_flash.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <spi-mem.h>
+#include "../mtd/spi/sf_internal.h"
DECLARE_GLOBAL_DATA_PTR;
@@ -41,6 +44,21 @@ DECLARE_GLOBAL_DATA_PTR;
#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst */
#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst */
#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst */
+#define ZYNQ_QSPI_FR_QOUT_CODE 0x6B /* read instruction code */
+
+#define QSPI_SELECT_LOWER_CS BIT(0)
+#define QSPI_SELECT_UPPER_CS BIT(1)
+
+/*
+ * QSPI Linear Configuration Register
+ *
+ * It is named Linear Configuration but it controls other modes when not in
+ * linear mode also.
+ */
+#define ZYNQ_QSPI_LCFG_TWO_MEM_MASK 0x40000000 /* QSPI Enable Bit Mask */
+#define ZYNQ_QSPI_LCFG_SEP_BUS_MASK 0x20000000 /* QSPI Enable Bit Mask */
+#define ZYNQ_QSPI_LCFG_U_PAGE 0x10000000 /* QSPI Upper memory set */
+#define ZYNQ_QSPI_LCFG_DUMMY_SHIFT 8
#define ZYNQ_QSPI_TXFIFO_THRESHOLD 1 /* Tx FIFO threshold level*/
#define ZYNQ_QSPI_RXFIFO_THRESHOLD 32 /* Rx FIFO threshold level */
@@ -100,7 +118,11 @@ struct zynq_qspi_priv {
int bytes_to_transfer;
int bytes_to_receive;
unsigned int is_inst;
+ unsigned int is_parallel;
+ unsigned int is_stacked;
+ unsigned int u_page;
unsigned cs_change:1;
+ unsigned is_strip:1;
};
static int zynq_qspi_of_to_plat(struct udevice *bus)
@@ -111,7 +133,6 @@ static int zynq_qspi_of_to_plat(struct udevice *bus)
plat->regs = (struct zynq_qspi_regs *)fdtdec_get_addr(blob,
node, "reg");
-
return 0;
}
@@ -146,6 +167,9 @@ static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv)
/* Disable Interrupts */
writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
+ /* Disable linear mode as the boot loader may have used it */
+ writel(0x0, &regs->lqspicfg);
+
/* Clear the TX and RX threshold reg */
writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, &regs->txftr);
writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, &regs->rxftr);
@@ -163,12 +187,11 @@ static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv)
confr |= ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
ZYNQ_QSPI_CR_MSTREN_MASK;
- writel(confr, &regs->cr);
- /* Disable the LQSPI feature */
- confr = readl(&regs->lqspicfg);
- confr &= ~ZYNQ_QSPI_LQSPICFG_LQMODE_MASK;
- writel(confr, &regs->lqspicfg);
+ if (priv->is_stacked)
+ confr |= 0x10;
+
+ writel(confr, &regs->cr);
/* Enable SPI */
writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
@@ -180,6 +203,7 @@ static int zynq_qspi_child_pre_probe(struct udevice *bus)
struct zynq_qspi_priv *priv = dev_get_priv(bus->parent);
priv->max_hz = slave->max_hz;
+ slave->multi_cs_cap = true;
return 0;
}
@@ -362,8 +386,8 @@ static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32 size)
unsigned len, offset;
struct zynq_qspi_regs *regs = priv->regs;
static const unsigned offsets[4] = {
- ZYNQ_QSPI_TXD_00_00_OFFSET, ZYNQ_QSPI_TXD_00_01_OFFSET,
- ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET };
+ ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET,
+ ZYNQ_QSPI_TXD_00_11_OFFSET, ZYNQ_QSPI_TXD_00_00_OFFSET };
while ((fifocount < size) &&
(priv->bytes_to_transfer > 0)) {
@@ -385,7 +409,11 @@ static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32 size)
return;
len = priv->bytes_to_transfer;
zynq_qspi_write_data(priv, &data, len);
- offset = (priv->rx_buf) ? offsets[0] : offsets[len];
+ if ((priv->is_parallel || priv->is_stacked) &&
+ !priv->is_inst && (len % 2))
+ len++;
+ offset = (priv->rx_buf) ?
+ offsets[3] : offsets[len - 1];
writel(data, &regs->cr + (offset / 4));
}
}
@@ -490,6 +518,7 @@ static int zynq_qspi_irq_poll(struct zynq_qspi_priv *priv)
*/
static int zynq_qspi_start_transfer(struct zynq_qspi_priv *priv)
{
+ static u8 current_u_page;
u32 data = 0;
struct zynq_qspi_regs *regs = priv->regs;
@@ -499,6 +528,34 @@ static int zynq_qspi_start_transfer(struct zynq_qspi_priv *priv)
priv->bytes_to_transfer = priv->len;
priv->bytes_to_receive = priv->len;
+ if (priv->is_parallel)
+ writel((ZYNQ_QSPI_LCFG_TWO_MEM_MASK |
+ ZYNQ_QSPI_LCFG_SEP_BUS_MASK |
+ (1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) |
+ ZYNQ_QSPI_FR_QOUT_CODE), &regs->lqspicfg);
+
+ if (priv->is_inst && priv->is_stacked && current_u_page != priv->u_page) {
+ if (priv->u_page) {
+ /* Configure two memories on shared bus
+ * by enabling upper mem
+ */
+ writel((ZYNQ_QSPI_LCFG_TWO_MEM_MASK |
+ ZYNQ_QSPI_LCFG_U_PAGE |
+ (1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) |
+ ZYNQ_QSPI_FR_QOUT_CODE),
+ &regs->lqspicfg);
+ } else {
+ /* Configure two memories on shared bus
+ * by enabling lower mem
+ */
+ writel((ZYNQ_QSPI_LCFG_TWO_MEM_MASK |
+ (1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) |
+ ZYNQ_QSPI_FR_QOUT_CODE),
+ &regs->lqspicfg);
+ }
+ current_u_page = priv->u_page;
+ }
+
if (priv->len < 4)
zynq_qspi_fill_tx_fifo(priv, priv->len);
else
@@ -585,20 +642,21 @@ static int zynq_qspi_xfer(struct udevice *dev, unsigned int bitlen,
struct zynq_qspi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
- priv->cs = slave_plat->cs;
+ priv->cs = slave_plat->cs[0];
priv->tx_buf = dout;
priv->rx_buf = din;
priv->len = bitlen / 8;
- debug("zynq_qspi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
- dev_seq(bus), slave_plat->cs, bitlen, priv->len, flags);
+ debug("%s: bus:%i cs[0]:%i bitlen:%i len:%i flags:%lx\n", __func__,
+ dev_seq(bus), slave_plat->cs[0], bitlen, priv->len, flags);
/*
* Festering sore.
* Assume that the beginning of a transfer with bits to
* transmit must contain a device command.
*/
- if (dout && flags & SPI_XFER_BEGIN)
+ if ((dout && flags & SPI_XFER_BEGIN) ||
+ (flags & SPI_XFER_END && !priv->is_strip))
priv->is_inst = 1;
else
priv->is_inst = 0;
@@ -608,6 +666,11 @@ static int zynq_qspi_xfer(struct udevice *dev, unsigned int bitlen,
else
priv->cs_change = 0;
+ if (flags & SPI_XFER_U_PAGE)
+ priv->u_page = 1;
+ else
+ priv->u_page = 0;
+
zynq_qspi_transfer(priv);
return 0;
@@ -671,14 +734,34 @@ static int zynq_qspi_set_mode(struct udevice *bus, uint mode)
return 0;
}
+bool update_stripe(const struct spi_mem_op *op)
+{
+ if (op->cmd.opcode == SPINOR_OP_BE_4K ||
+ op->cmd.opcode == SPINOR_OP_CHIP_ERASE ||
+ op->cmd.opcode == SPINOR_OP_SE ||
+ op->cmd.opcode == SPINOR_OP_WREAR
+ )
+ return false;
+
+ return true;
+}
+
static int zynq_qspi_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
+ struct udevice *bus = slave->dev->parent;
+ struct zynq_qspi_priv *priv = dev_get_priv(bus);
int op_len, pos = 0, ret, i;
unsigned int flag = 0;
const u8 *tx_buf = NULL;
u8 *rx_buf = NULL;
+ if ((slave->flags & QSPI_SELECT_LOWER_CS) &&
+ (slave->flags & QSPI_SELECT_UPPER_CS))
+ priv->is_parallel = true;
+ if (slave->flags & SPI_XFER_STACKED)
+ priv->is_stacked = true;
+
if (op->data.nbytes) {
if (op->data.dir == SPI_MEM_DATA_IN)
rx_buf = op->data.buf.in;
@@ -703,6 +786,9 @@ static int zynq_qspi_exec_op(struct spi_slave *slave,
if (op->dummy.nbytes)
memset(op_buf + pos, 0xff, op->dummy.nbytes);
+ if (slave->flags & SPI_XFER_U_PAGE)
+ flag |= SPI_XFER_U_PAGE;
+
/* 1st transfer: opcode + address + dummy cycles */
/* Make sure to set END bit if no tx or rx data messages follow */
if (!tx_buf && !rx_buf)
@@ -713,6 +799,8 @@ static int zynq_qspi_exec_op(struct spi_slave *slave,
if (ret)
return ret;
+ priv->is_strip = update_stripe(op);
+
/* 2nd transfer: rx or tx data path */
if (tx_buf || rx_buf) {
ret = zynq_qspi_xfer(slave->dev, op->data.nbytes * 8, tx_buf,
@@ -721,6 +809,9 @@ static int zynq_qspi_exec_op(struct spi_slave *slave,
return ret;
}
+ priv->is_parallel = false;
+ priv->is_stacked = false;
+ slave->flags &= ~SPI_XFER_MASK;
spi_release_bus(slave);
return 0;
diff --git a/drivers/spi/zynq_spi.c b/drivers/spi/zynq_spi.c
index ebcb5b6..53b59ae 100644
--- a/drivers/spi/zynq_spi.c
+++ b/drivers/spi/zynq_spi.c
@@ -241,15 +241,15 @@ static int zynq_spi_xfer(struct udevice *dev, unsigned int bitlen,
u8 *rx_buf = din, buf;
u32 ts, status;
- debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
- dev_seq(bus), slave_plat->cs, bitlen, len, flags);
+ debug("spi_xfer: bus:%i cs[0]:%i bitlen:%i len:%i flags:%lx\n",
+ dev_seq(bus), slave_plat->cs[0], bitlen, len, flags);
if (bitlen % 8) {
debug("spi_xfer: Non byte aligned SPI transfer\n");
return -1;
}
- priv->cs = slave_plat->cs;
+ priv->cs = slave_plat->cs[0];
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c
index 61349a4..28eadff 100644
--- a/drivers/spi/zynqmp_gqspi.c
+++ b/drivers/spi/zynqmp_gqspi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * (C) Copyright 2018 Xilinx
- *
+ * (C) Copyright 2013 - 2022, Xilinx, Inc.
+ * (C) Copyright 2023, Advanced Micro Devices, Inc.
* Xilinx ZynqMP Generic Quad-SPI(QSPI) controller driver(master mode only)
*/
@@ -24,6 +24,8 @@
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/sizes.h>
+#include <linux/mtd/spi-nor.h>
+#include "../mtd/spi/sf_internal.h"
#include <zynqmp_firmware.h>
#define GQSPI_GFIFO_STRT_MODE_MASK BIT(29)
@@ -87,6 +89,9 @@
#define SPI_XFER_ON_LOWER 1
#define SPI_XFER_ON_UPPER 2
+#define GQSPI_SELECT_LOWER_CS BIT(0)
+#define GQSPI_SELECT_UPPER_CS BIT(1)
+
#define GQSPI_DMA_ALIGN 0x4
#define GQSPI_MAX_BAUD_RATE_VAL 7
#define GQSPI_DFLT_BAUD_RATE_VAL 2
@@ -182,13 +187,14 @@ struct zynqmp_qspi_priv {
int bytes_to_transfer;
int bytes_to_receive;
const struct spi_mem_op *op;
+ unsigned int is_parallel;
+ unsigned int u_page;
+ unsigned int bus;
+ unsigned int stripe;
+ unsigned int flags;
+ u32 max_hz;
};
-__weak int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value)
-{
- return 0;
-}
-
static int zynqmp_qspi_of_to_plat(struct udevice *bus)
{
struct zynqmp_qspi_plat *plat = dev_get_plat(bus);
@@ -233,8 +239,30 @@ static u32 zynqmp_qspi_bus_select(struct zynqmp_qspi_priv *priv)
{
u32 gqspi_fifo_reg = 0;
- gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
- GQSPI_GFIFO_CS_LOWER;
+ if (priv->is_parallel) {
+ if (priv->bus == SPI_XFER_ON_BOTH)
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_UP_BUS |
+ GQSPI_GFIFO_CS_UPPER |
+ GQSPI_GFIFO_CS_LOWER;
+ else if (priv->bus == SPI_XFER_ON_LOWER)
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_CS_UPPER |
+ GQSPI_GFIFO_CS_LOWER;
+ else if (priv->bus == SPI_XFER_ON_UPPER)
+ gqspi_fifo_reg = GQSPI_GFIFO_UP_BUS |
+ GQSPI_GFIFO_CS_LOWER |
+ GQSPI_GFIFO_CS_UPPER;
+ else
+ debug("Wrong Bus selection:0x%x\n", priv->bus);
+ } else {
+ if (priv->u_page)
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_CS_UPPER;
+ else
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_CS_LOWER;
+ }
return gqspi_fifo_reg;
}
@@ -294,8 +322,15 @@ static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on)
gqspi_fifo_reg |= GQSPI_SPI_MODE_SPI |
GQSPI_IMD_DATA_CS_ASSERT;
} else {
- gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS;
- gqspi_fifo_reg |= GQSPI_IMD_DATA_CS_DEASSERT;
+ if (priv->is_parallel) {
+ gqspi_fifo_reg = GQSPI_GFIFO_UP_BUS |
+ GQSPI_GFIFO_LOW_BUS;
+ } else if (priv->u_page) {
+ gqspi_fifo_reg = GQSPI_GFIFO_UP_BUS;
+ } else {
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS;
+ gqspi_fifo_reg |= GQSPI_IMD_DATA_CS_DEASSERT;
+ }
}
zynqmp_qspi_fill_gen_fifo(priv, gqspi_fifo_reg);
@@ -365,12 +400,13 @@ static int zynqmp_qspi_set_speed(struct udevice *bus, uint speed)
log_debug("%s, Speed: %d, Max: %d\n", __func__, speed, plat->frequency);
- if (speed > plat->frequency)
- speed = plat->frequency;
+ /*
+ * If speed == 0 or speed > max freq, then set speed to highest
+ */
+ if (!speed || speed > priv->max_hz)
+ speed = priv->max_hz;
if (plat->speed_hz != speed) {
- /* Set the clock frequency */
- /* If speed == 0, default to lowest speed */
while ((baud_rate_val < 8) &&
((plat->frequency /
(2 << baud_rate_val)) > speed))
@@ -392,6 +428,18 @@ static int zynqmp_qspi_set_speed(struct udevice *bus, uint speed)
return 0;
}
+static int zynqmp_qspi_child_pre_probe(struct udevice *bus)
+{
+ struct spi_slave *slave = dev_get_parent_priv(bus);
+ struct zynqmp_qspi_priv *priv = dev_get_priv(bus->parent);
+
+ slave->multi_cs_cap = true;
+ slave->bytemode = SPI_4BYTE_MODE;
+ priv->max_hz = slave->max_hz;
+
+ return 0;
+}
+
static int zynqmp_qspi_probe(struct udevice *bus)
{
struct zynqmp_qspi_plat *plat = dev_get_plat(bus);
@@ -456,12 +504,17 @@ static int zynqmp_qspi_set_mode(struct udevice *bus, uint mode)
static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size)
{
- u32 data;
+ u32 data, ier;
int ret = 0;
struct zynqmp_qspi_regs *regs = priv->regs;
u32 *buf = (u32 *)priv->tx_buf;
u32 len = size;
+ /* Enable interrupts */
+ ier = readl(&regs->ier);
+ ier |= GQSPI_IXR_ALL_MASK | GQSPI_IXR_TXFIFOEMPTY_MASK;
+ writel(ier, &regs->ier);
+
while (size) {
ret = wait_for_bit_le32(&regs->isr, GQSPI_IXR_TXNFULL_MASK, 1,
GQSPI_TIMEOUT, 1);
@@ -584,6 +637,9 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth);
gen_fifo_cmd |= GQSPI_GFIFO_TX | GQSPI_GFIFO_DATA_XFR_MASK;
+ if (priv->stripe)
+ gen_fifo_cmd |= GQSPI_GFIFO_STRIPE_MASK;
+
while (priv->len) {
len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
@@ -718,6 +774,9 @@ static int zynqmp_qspi_genfifo_fill_rx(struct zynqmp_qspi_priv *priv)
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth);
gen_fifo_cmd |= GQSPI_GFIFO_RX | GQSPI_GFIFO_DATA_XFR_MASK;
+ if (priv->stripe)
+ gen_fifo_cmd |= GQSPI_GFIFO_STRIPE_MASK;
+
/*
* Check if receive buffer is aligned to 4 byte and length
* is multiples of four byte as we are using dma to receive.
@@ -758,6 +817,33 @@ static int zynqmp_qspi_release_bus(struct udevice *dev)
return 0;
}
+static bool zynqmp_qspi_update_stripe(const struct spi_mem_op *op)
+{
+ /*
+ * This is a list of opcodes for which we must not use striped access
+ * even in dual parallel mode, but instead broadcast the same data to
+ * both chips. This is primarily erase commands and writing some
+ * registers.
+ */
+ switch (op->cmd.opcode) {
+ case SPINOR_OP_BE_4K:
+ case SPINOR_OP_BE_32K:
+ case SPINOR_OP_CHIP_ERASE:
+ case SPINOR_OP_SE:
+ case SPINOR_OP_BE_32K_4B:
+ case SPINOR_OP_SE_4B:
+ case SPINOR_OP_BE_4K_4B:
+ case SPINOR_OP_WRSR:
+ case SPINOR_OP_WREAR:
+ case SPINOR_OP_BRWR:
+ return false;
+ case SPINOR_OP_WRSR2:
+ return op->addr.nbytes != 0;
+ default:
+ return true;
+ }
+}
+
static int zynqmp_qspi_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
@@ -769,6 +855,25 @@ static int zynqmp_qspi_exec_op(struct spi_slave *slave,
priv->rx_buf = op->data.buf.in;
priv->len = op->data.nbytes;
+ if (slave->flags & SPI_XFER_U_PAGE)
+ priv->u_page = 1;
+ else
+ priv->u_page = 0;
+
+ if ((slave->flags & GQSPI_SELECT_LOWER_CS) &&
+ (slave->flags & GQSPI_SELECT_UPPER_CS))
+ priv->is_parallel = true;
+
+ priv->stripe = 0;
+ priv->bus = 0;
+
+ if (priv->is_parallel) {
+ if (slave->flags & SPI_XFER_MASK)
+ priv->bus = (slave->flags & SPI_XFER_MASK) >> 8;
+ if (zynqmp_qspi_update_stripe(op))
+ priv->stripe = 1;
+ }
+
zynqmp_qspi_chipselect(priv, 1);
/* Send opcode, addr, dummy */
@@ -782,6 +887,9 @@ static int zynqmp_qspi_exec_op(struct spi_slave *slave,
zynqmp_qspi_chipselect(priv, 0);
+ priv->is_parallel = false;
+ slave->flags &= ~SPI_XFER_MASK;
+
return ret;
}
@@ -812,4 +920,5 @@ U_BOOT_DRIVER(zynqmp_qspi) = {
.plat_auto = sizeof(struct zynqmp_qspi_plat),
.priv_auto = sizeof(struct zynqmp_qspi_priv),
.probe = zynqmp_qspi_probe,
+ .child_pre_probe = zynqmp_qspi_child_pre_probe,
};
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index d1dbf3e..b0e9df4 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -13,6 +13,9 @@
#include <linux/mtd/mtd.h>
#include <spi-mem.h>
+/* In parallel configuration enable multiple CS */
+#define SPI_NOR_ENABLE_MULTI_CS (BIT(0) | BIT(1))
+
/*
* Manufacturer IDs
*
@@ -177,6 +180,12 @@
/* Status Register 2 bits. */
#define SR2_QUAD_EN_BIT7 BIT(7)
+/*
+ * Maximum number of flashes that can be connected
+ * in stacked/parallel configuration
+ */
+#define SNOR_FLASH_CNT_MAX 2
+
/* For Cypress flash. */
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
@@ -294,6 +303,8 @@ enum spi_nor_option_flags {
SNOR_F_BROKEN_RESET = BIT(6),
SNOR_F_SOFT_RESET = BIT(7),
SNOR_F_IO_MODE_EN_VOLATILE = BIT(8),
+ SNOR_F_HAS_STACKED = BIT(9),
+ SNOR_F_HAS_PARALLEL = BIT(10),
};
struct spi_nor;
@@ -551,6 +562,7 @@ struct spi_nor {
u8 bank_read_cmd;
u8 bank_write_cmd;
u8 bank_curr;
+ u8 upage_prev;
#endif
enum spi_nor_protocol read_proto;
enum spi_nor_protocol write_proto;
diff --git a/include/spi.h b/include/spi.h
index 7e38cc2..ee0f86f 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -38,6 +38,18 @@
#define SPI_DEFAULT_WORDLEN 8
+#define SPI_3BYTE_MODE 0x0
+#define SPI_4BYTE_MODE 0x1
+
+/* SPI transfer flags */
+#define SPI_XFER_STRIPE (1 << 6)
+#define SPI_XFER_MASK (3 << 8)
+#define SPI_XFER_LOWER (1 << 8)
+#define SPI_XFER_UPPER (2 << 8)
+
+/* Max no. of CS supported per spi device */
+#define SPI_CS_CNT_MAX 2
+
/**
* struct dm_spi_bus - SPI bus info
*
@@ -71,7 +83,7 @@ struct dm_spi_bus {
* @mode: SPI mode to use for this device (see SPI mode flags)
*/
struct dm_spi_slave_plat {
- unsigned int cs;
+ unsigned int cs[SPI_CS_CNT_MAX];
uint max_hz;
uint mode;
};
@@ -155,6 +167,15 @@ struct spi_slave {
#define SPI_XFER_BEGIN BIT(0) /* Assert CS before transfer */
#define SPI_XFER_END BIT(1) /* Deassert CS after transfer */
#define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END)
+#define SPI_XFER_U_PAGE BIT(4)
+#define SPI_XFER_STACKED BIT(5)
+ /*
+ * Flag indicating that the spi-controller has multi chip select
+ * capability and can assert/de-assert more than one chip select
+ * at once.
+ */
+ bool multi_cs_cap;
+ u32 bytemode;
};
/**
diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c
index ed94194..0f3044b 100644
--- a/lib/acpi/acpi_device.c
+++ b/lib/acpi/acpi_device.c
@@ -728,7 +728,7 @@ static int acpi_device_set_spi(const struct udevice *dev, struct acpi_spi *spi,
plat = dev_get_parent_plat(slave->dev);
memset(spi, '\0', sizeof(*spi));
- spi->device_select = plat->cs;
+ spi->device_select = plat->cs[0];
spi->device_select_polarity = SPI_POLARITY_LOW;
spi->wire_mode = SPI_4_WIRE_MODE;
spi->speed = plat->max_hz;