From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
To: <broonie@kernel.org>, <tudor.ambarus@linaro.org>,
<pratyush@kernel.org>, <miquel.raynal@bootlin.com>,
<richard@nod.at>, <vigneshr@ti.com>,
<sbinding@opensource.cirrus.com>, <lee@kernel.org>,
<james.schulman@cirrus.com>, <david.rhodes@cirrus.com>,
<rf@opensource.cirrus.com>, <perex@perex.cz>, <tiwai@suse.com>
Cc: <linux-spi@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<michael@walle.cc>, <linux-mtd@lists.infradead.org>,
<nicolas.ferre@microchip.com>, <alexandre.belloni@bootlin.com>,
<claudiu.beznea@tuxon.dev>, <michal.simek@amd.com>,
<linux-arm-kernel@lists.infradead.org>,
<alsa-devel@alsa-project.org>, <patches@opensource.cirrus.com>,
<linux-sound@vger.kernel.org>, <git@amd.com>,
<amitrkcian2002@gmail.com>,
Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
Subject: [PATCH v11 09/10] mtd: spi-nor: Add parallel memories support in spi-nor
Date: Sat, 25 Nov 2023 14:51:36 +0530 [thread overview]
Message-ID: <20231125092137.2948-10-amit.kumar-mahapatra@amd.com> (raw)
In-Reply-To: <20231125092137.2948-1-amit.kumar-mahapatra@amd.com>
The current implementation assumes that a maximum of two flashes are
connected in parallel mode. 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->spimem->spi->cs_index_mask. The QSPI driver will then assert/de-assert
CS0 & CS1.
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.
Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
---
drivers/mtd/spi-nor/core.c | 409 +++++++++++++++++++++++++-------
drivers/mtd/spi-nor/core.h | 4 +
drivers/mtd/spi-nor/micron-st.c | 5 +
3 files changed, 333 insertions(+), 85 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index e990be7c7eb6..2e6cc45341ed 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -470,17 +470,29 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
op.data.nbytes = 2;
}
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ op.data.nbytes = 2;
+
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr,
- 1);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ ret = spi_nor_controller_ops_read_reg(nor,
+ SPINOR_OP_RDSR,
+ sr, 2);
+ else
+ ret = spi_nor_controller_ops_read_reg(nor,
+ SPINOR_OP_RDSR,
+ sr, 1);
}
if (ret)
dev_dbg(nor->dev, "error %d reading SR\n", ret);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ sr[0] |= sr[1];
+
return ret;
}
@@ -1824,6 +1836,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
struct spi_nor_flash_parameter *params;
u32 addr, len, offset, cur_cs_num = 0;
uint32_t rem;
+ u32 n_flash = 1;
int ret;
u64 sz;
@@ -1833,6 +1846,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
params = spi_nor_get_params(nor, 0);
sz = params->size;
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
if (spi_nor_has_uniform_erase(nor)) {
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
@@ -1854,53 +1870,82 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
if (ret)
goto erase_err;
- while (cur_cs_num < nor->num_flash) {
- nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
ret = spi_nor_erase_chip(nor);
spi_nor_unlock_device(nor);
if (ret)
goto erase_err;
/*
- * Scale the timeout linearly with the size of the flash, with
- * a minimum calibrated to an old 2MB flash. We could try to
- * pull these from CFI/SFDP, but these values should be good
- * enough for now.
- */
+ * Scale the timeout linearly with the size of the flash, with
+ * a minimum calibrated to an old 2MB flash. We could try to
+ * pull these from CFI/SFDP, but these values should be good
+ * enough for now.
+ */
timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
- CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
- (unsigned long)(params->size / SZ_2M));
+ CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
+ (unsigned long)(mtd->size / SZ_2M));
ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
if (ret)
goto erase_err;
- cur_cs_num++;
- }
+ } else {
+ while (cur_cs_num < n_flash) {
+ nor->spimem->spi->cs_index_mask = 1 << cur_cs_num;
+ ret = spi_nor_erase_chip(nor);
+ spi_nor_unlock_device(nor);
+ if (ret)
+ goto erase_err;
+
+ /*
+ * Scale the timeout linearly with the size of the flash, with
+ * a minimum calibrated to an old 2MB flash. We could try to
+ * pull these from CFI/SFDP, but these values should be good
+ * enough for now.
+ */
+ timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
+ CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
+ (unsigned long)(params->size / SZ_2M));
+ ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
+ if (ret)
+ goto erase_err;
+
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ }
+ }
/* REVISIT in some cases we could speed up erasing large regions
* by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
* to use "small sector erase", but that's not always optimal.
*/
-
/* "sector"-at-a-time erase */
} else if (spi_nor_has_uniform_erase(nor)) {
- /* Determine the flash from which the operation need to start */
- while ((cur_cs_num < nor->num_flash) && (addr > sz - 1)) {
- cur_cs_num++;
- params = spi_nor_get_params(nor, cur_cs_num);
- sz += params->size;
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL)) {
+ while ((cur_cs_num < n_flash) && (addr > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
}
-
while (len) {
ret = spi_nor_lock_device(nor);
if (ret)
goto erase_err;
- nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
offset = addr;
if (nor->flags & SNOR_F_HAS_STACKED) {
params = spi_nor_get_params(nor, cur_cs_num);
offset -= (sz - params->size);
}
+ nor->spimem->spi->cs_index_mask = 1 << cur_cs_num;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ u64 aux = offset;
+
+ ret = do_div(aux, n_flash);
+ offset = aux;
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ }
ret = spi_nor_erase_sector(nor, offset);
spi_nor_unlock_device(nor);
@@ -1923,34 +1968,42 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
sz += params->size;
}
}
-
/* erase multiple sectors */
} else {
- u64 erase_len = 0;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ u64 aux = offset;
- /* Determine the flash from which the operation need to start */
- while ((cur_cs_num < nor->num_flash) && (addr > sz - 1)) {
- cur_cs_num++;
- params = spi_nor_get_params(nor, cur_cs_num);
- sz += params->size;
- }
- /* perform multi sector erase onec per Flash*/
- while (len) {
- erase_len = (len > (sz - addr)) ? (sz - addr) : len;
- offset = addr;
- nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
- if (nor->flags & SNOR_F_HAS_STACKED) {
- params = spi_nor_get_params(nor, cur_cs_num);
- offset -= (sz - params->size);
- }
- ret = spi_nor_erase_multi_sectors(nor, offset, erase_len);
+ ret = do_div(aux, n_flash);
+ offset = aux;
+ ret = spi_nor_erase_multi_sectors(nor, addr, len);
if (ret)
goto erase_err;
- len -= erase_len;
- addr += erase_len;
- cur_cs_num++;
- params = spi_nor_get_params(nor, cur_cs_num);
- sz += params->size;
+ } else {
+ u64 erase_len = 0;
+
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < n_flash) && (addr > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
+ /* perform multi sector erase onec per Flash*/
+ while (len) {
+ erase_len = (len > (sz - addr)) ? (sz - addr) : len;
+ offset = addr;
+ nor->spimem->spi->cs_index_mask = 1 << cur_cs_num;
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ params = spi_nor_get_params(nor, cur_cs_num);
+ offset -= (sz - params->size);
+ }
+ ret = spi_nor_erase_multi_sectors(nor, offset, erase_len);
+ if (ret)
+ goto erase_err;
+ len -= erase_len;
+ addr += erase_len;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
}
}
@@ -2144,9 +2197,12 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
struct spi_nor *nor = mtd_to_spi_nor(mtd);
struct spi_nor_flash_parameter *params;
ssize_t ret, read_len, len_lock = len;
+ bool is_ofst_odd = false;
loff_t from_lock = from;
u32 cur_cs_num = 0;
- u64 sz;
+ u_char *readbuf;
+ u32 n_flash = 1;
+ u64 sz = 0;
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
@@ -2157,23 +2213,54 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
params = spi_nor_get_params(nor, 0);
sz = params->size;
- /* Determine the flash from which the operation need to start */
- while ((cur_cs_num < nor->num_flash) && (from > sz - 1)) {
- cur_cs_num++;
- params = spi_nor_get_params(nor, cur_cs_num);
- sz += params->size;
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
+ /*
+ * When even number of flashes are connected in parallel and the
+ * requested read length is odd then read (len + 1) from offset + 1
+ * and ignore offset[0] data.
+ */
+ if ((nor->flags & SNOR_F_HAS_PARALLEL) && (!(n_flash % 2)) && (from & 0x01)) {
+ from = (loff_t)(from - 1);
+ len = (size_t)(len + 1);
+ is_ofst_odd = true;
+ readbuf = kmalloc(len, GFP_KERNEL);
+ if (!readbuf)
+ return -ENOMEM;
+ } else {
+ readbuf = buf;
}
+
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL)) {
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < n_flash) && (from > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
+ }
+
while (len) {
loff_t addr = from;
- nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
- read_len = (len > (sz - addr)) ? (sz - addr) : len;
- params = spi_nor_get_params(nor, cur_cs_num);
- addr -= (sz - params->size);
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ u64 aux = addr;
+
+ ret = do_div(aux, n_flash);
+ addr = aux;
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ read_len = len;
+ } else {
+ nor->spimem->spi->cs_index_mask = 1 << cur_cs_num;
+ read_len = (len > (sz - addr)) ? (sz - addr) : len;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ addr -= (sz - params->size);
+ }
addr = spi_nor_convert_addr(nor, addr);
- ret = spi_nor_read_data(nor, addr, len, buf);
+ ret = spi_nor_read_data(nor, addr, read_len, readbuf);
if (ret == 0) {
/* We shouldn't see 0-length reads */
ret = -EIO;
@@ -2183,8 +2270,20 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
goto read_err;
WARN_ON(ret > read_len);
- *retlen += ret;
+ if (is_ofst_odd) {
+ /*
+ * Cannot read from odd offset in parallel mode.
+ * So read len + 1 from offset + 1 from the flash
+ * and copy len data from readbuf[1].
+ */
+ memcpy(buf, (readbuf + 1), (len - 1));
+ *retlen += (ret - 1);
+ } else {
+ *retlen += ret;
+ }
buf += ret;
+ if (!is_ofst_odd)
+ readbuf += ret;
from += ret;
len -= ret;
@@ -2202,8 +2301,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
ret = 0;
read_err:
- spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock);
+ if (is_ofst_odd)
+ kfree(readbuf);
+ spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock);
return ret;
}
@@ -2219,6 +2320,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
struct spi_nor_flash_parameter *params;
size_t page_offset, page_remain, i;
u32 page_size, cur_cs_num = 0;
+ u32 n_flash = 1;
ssize_t ret;
u64 sz;
@@ -2232,11 +2334,41 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
page_size = params->page_size;
sz = params->size;
- /* Determine the flash from which the operation need to start */
- while ((cur_cs_num < nor->num_flash) && (to > sz - 1)) {
- cur_cs_num++;
- params = spi_nor_get_params(nor, cur_cs_num);
- sz += params->size;
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ /*
+ * When even number of flashes are connected in parallel and the
+ * requested write length is odd then first write 2 bytes.
+ */
+ if ((!(n_flash % 2)) && (to & 0x01)) {
+ u8 two[2] = {0xff, buf[0]};
+ size_t written_len;
+
+ ret = spi_nor_write(mtd, to & ~1, 2, &written_len, two);
+ if (ret < 0)
+ return ret;
+ *retlen += 1; /* We've written only one actual byte */
+ ++buf;
+ --len;
+ ++to;
+ }
+ /*
+ * Write operation are performed in page size chunks and in
+ * parallel memories both the flashes are written simultaneously,
+ * hence increase the page_size in multiple of the number of flash
+ * connected in parallel.
+ */
+ page_size *= n_flash;
+
+ } else {
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < n_flash) && (to > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
}
for (i = 0; i < len; ) {
@@ -2258,9 +2390,17 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
/* the size of data remaining on the first page */
page_remain = min_t(size_t, page_size - page_offset, len - i);
- nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
- params = spi_nor_get_params(nor, cur_cs_num);
- addr -= (sz - params->size);
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ u64 aux = addr;
+
+ ret = do_div(aux, n_flash);
+ addr = aux;
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ } else {
+ nor->spimem->spi->cs_index_mask = 1 << cur_cs_num;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ addr -= (sz - params->size);
+ }
addr = spi_nor_convert_addr(nor, addr);
@@ -2701,7 +2841,15 @@ static int spi_nor_select_erase(struct spi_nor *nor)
if (!erase)
return -EINVAL;
nor->erase_opcode = erase->opcode;
- mtd->erasesize = erase->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 = erase->size * 2;
+ else
+ mtd->erasesize = erase->size;
return 0;
}
@@ -2719,7 +2867,15 @@ static int spi_nor_select_erase(struct spi_nor *nor)
if (!erase)
return -EINVAL;
- mtd->erasesize = erase->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 = erase->size * 2;
+ else
+ mtd->erasesize = erase->size;
return 0;
}
@@ -3060,6 +3216,17 @@ static int spi_nor_late_init_params(struct spi_nor *nor)
nor->num_flash++;
}
+ idx = 0;
+ while (idx < SNOR_FLASH_CNT_MAX) {
+ rc = of_property_read_u64_index(np, "parallel-memories", idx, &flash_size[idx]);
+ if (rc)
+ break;
+ idx++;
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL))
+ nor->flags |= SNOR_F_HAS_PARALLEL;
+
+ nor->num_flash++;
+ }
/*
* By default one flash device should be connected
@@ -3068,7 +3235,7 @@ static int spi_nor_late_init_params(struct spi_nor *nor)
if (!nor->num_flash)
nor->num_flash = 1;
- if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (nor->flags & (SNOR_F_HAS_STACKED | SNOR_F_HAS_PARALLEL)) {
for (idx = 1; idx < nor->num_flash; idx++) {
params = spi_nor_get_params(nor, idx);
params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
@@ -3289,10 +3456,14 @@ static int spi_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
static int spi_nor_quad_enable(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params;
+ u32 n_flash = 1;
int err, idx;
- for (idx = 0; idx < nor->num_flash; idx++) {
- params = spi_nor_get_params(nor, idx);
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ params = spi_nor_get_params(nor, 0);
if (!params->quad_enable)
return 0;
@@ -3300,14 +3471,32 @@ static int spi_nor_quad_enable(struct spi_nor *nor)
spi_nor_get_protocol_width(nor->write_proto) == 4))
return 0;
/*
- * Set the appropriate CS index before
- * issuing the command.
+ * In parallel mode both chip selects i.e., CS0 &
+ * CS1 need to be asserted simulatneously.
*/
- nor->spimem->spi->cs_index_mask = 0x01 << idx;
-
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
err = params->quad_enable(nor);
if (err)
return err;
+ } else {
+ for (idx = 0; idx < n_flash; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (!params->quad_enable)
+ return 0;
+
+ if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4))
+ return 0;
+ /*
+ * Set the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 1 << idx;
+
+ err = params->quad_enable(nor);
+ if (err)
+ return err;
+ }
}
return err;
}
@@ -3341,8 +3530,12 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
static int spi_nor_init(struct spi_nor *nor)
{
+ u32 n_flash = 1;
int err, idx;
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
err = spi_nor_set_octal_dtr(nor, true);
if (err) {
dev_dbg(nor->dev, "octal mode not supported\n");
@@ -3382,15 +3575,26 @@ static int spi_nor_init(struct spi_nor *nor)
*/
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n");
- for (idx = 0; idx < nor->num_flash; idx++) {
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
/*
- * Select the appropriate CS index before
- * issuing the command.
+ * In parallel mode both chip selects i.e., CS0 &
+ * CS1 need to be asserted simulatneously.
*/
- nor->spimem->spi->cs_index_mask = 0x01 << idx;
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
err = spi_nor_set_4byte_addr_mode(nor, true);
if (err)
return err;
+ } else {
+ for (idx = 0; idx < n_flash; idx++) {
+ /*
+ * Select the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 1 << idx;
+ err = spi_nor_set_4byte_addr_mode(nor, true);
+ if (err)
+ return err;
+ }
}
}
@@ -3505,19 +3709,23 @@ static void spi_nor_put_device(struct mtd_info *mtd)
static void spi_nor_restore(struct spi_nor *nor)
{
+ u32 n_flash = 1;
int ret;
int idx;
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
/* restore the addressing mode */
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET) {
- for (idx = 0; idx < nor->num_flash; idx++) {
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
/*
- * Select the appropriate CS index before
- * issuing the command.
+ * In parallel mode both chip selects i.e., CS0 &
+ * CS1 need to be asserted simulatneously.
*/
- nor->spimem->spi->cs_index_mask = 1 << idx;
- ret = spi_nor_set_4byte_addr_mode(nor, false);
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ spi_nor_set_4byte_addr_mode(nor, false);
if (ret)
/*
* Do not stop the execution in the hope that the flash
@@ -3527,6 +3735,24 @@ static void spi_nor_restore(struct spi_nor *nor)
dev_err(nor->dev,
"Failed to exit 4-byte address mode, err = %d\n",
ret);
+ } else {
+ for (idx = 0; idx < n_flash; idx++) {
+ /*
+ * Select the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 1 << idx;
+ ret = spi_nor_set_4byte_addr_mode(nor, false);
+ if (ret)
+ /*
+ * Do not stop the execution in the hope that the
+ * flash will default to the 3-byte address mode
+ * after the software reset.
+ */
+ dev_err(nor->dev,
+ "Failed to exit 4-byte address mode, err = %d\n",
+ ret);
+ }
}
}
@@ -3595,11 +3821,15 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
struct mtd_info *mtd = &nor->mtd;
struct device *dev = nor->dev;
u64 total_sz = 0;
+ u32 n_flash = 1;
int idx;
spi_nor_set_mtd_locking_ops(nor);
spi_nor_set_mtd_otp_ops(nor);
+ if (nor->num_flash)
+ n_flash = nor->num_flash;
+
mtd->dev.parent = dev;
if (!mtd->name)
mtd->name = dev_name(dev);
@@ -3613,8 +3843,17 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
else
mtd->_erase = spi_nor_erase;
mtd->writesize = params->writesize;
- mtd->writebufsize = params->page_size;
- for (idx = 0; idx < nor->num_flash; idx++) {
+ /*
+ * In parallel-memories the write operation is
+ * performed on both the flashes simultaneously
+ * one page per flash, so double the writebufsize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->writebufsize = params->page_size << 1;
+ else
+ mtd->writebufsize = params->page_size;
+
+ for (idx = 0; idx < n_flash; idx++) {
params = spi_nor_get_params(nor, idx);
total_sz += params->size;
}
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index b2997eca7551..6c3009796013 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -21,6 +21,9 @@
/* In single configuration enable CS0 */
#define SPI_NOR_ENABLE_CS0 BIT(0)
+/* In parallel configuration enable multiple CS */
+#define SPI_NOR_ENABLE_MULTI_CS (BIT(0) | BIT(1))
+
/* Standard SPI NOR flash operations. */
#define SPI_NOR_READID_OP(naddr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 0), \
@@ -144,6 +147,7 @@ enum spi_nor_option_flags {
SNOR_F_ECC = BIT(15),
SNOR_F_NO_WP = BIT(16),
SNOR_F_HAS_STACKED = BIT(17),
+ SNOR_F_HAS_PARALLEL = BIT(18),
};
struct spi_nor_read_command {
diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c
index 813a5b1a269b..577121ebd6e8 100644
--- a/drivers/mtd/spi-nor/micron-st.c
+++ b/drivers/mtd/spi-nor/micron-st.c
@@ -478,6 +478,9 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
op.data.nbytes = 2;
}
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ op.data.nbytes = 2;
+
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
@@ -489,6 +492,8 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
if (ret)
dev_dbg(nor->dev, "error %d reading FSR\n", ret);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ fsr[0] &= fsr[1];
return ret;
}
--
2.17.1
next prev parent reply other threads:[~2023-11-25 9:22 UTC|newest]
Thread overview: 78+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-25 9:21 [PATCH v11 00/10] spi: Add support for stacked/parallel memories Amit Kumar Mahapatra
2023-11-25 9:21 ` [PATCH v11 01/10] mfd: tps6594: Use set/get APIs to access spi->chip_select Amit Kumar Mahapatra
2023-12-01 9:57 ` (subset) " Lee Jones
2023-12-01 18:50 ` Mark Brown
2023-12-06 13:45 ` Lee Jones
2023-12-07 13:38 ` [GIT PULL] Immutable branch between MFD and SPI due for the v6.8 merge window Lee Jones
2023-12-07 16:20 ` Mark Brown
2023-11-25 9:21 ` [PATCH v11 02/10] ALSA: hda/cs35l56: Use set/get APIs to access spi->chip_select Amit Kumar Mahapatra
2023-11-25 9:21 ` [PATCH v11 03/10] spi: Add multi-cs memories support in SPI core Amit Kumar Mahapatra
2024-01-12 19:11 ` Guenter Roeck
2024-01-12 19:16 ` Mark Brown
2024-01-12 20:05 ` Guenter Roeck
2024-01-20 17:05 ` Guenter Roeck
2024-01-21 1:04 ` Mark Brown
2024-01-21 16:58 ` Guenter Roeck
2024-01-21 18:06 ` Michael Walle
2024-01-21 19:29 ` Guenter Roeck
2024-01-21 21:17 ` Mark Brown
2024-01-21 21:15 ` Mark Brown
2024-01-21 9:42 ` Linux regression tracking #adding (Thorsten Leemhuis)
2024-01-23 15:45 ` Linux regression tracking #update (Thorsten Leemhuis)
2023-11-25 9:21 ` [PATCH v11 04/10] mtd: spi-nor: Convert macros with inline functions Amit Kumar Mahapatra
2023-11-25 9:21 ` [PATCH v11 05/10] mtd: spi-nor: Add APIs to set/get nor->params Amit Kumar Mahapatra
2023-11-25 9:21 ` [PATCH v11 06/10] mtd: spi-nor: Move write enable inside specific write & erase APIs Amit Kumar Mahapatra
2023-11-25 9:21 ` [PATCH v11 07/10] mtd: spi-nor: Add stacked memories support in spi-nor Amit Kumar Mahapatra
2023-12-06 14:30 ` Tudor Ambarus
2023-12-06 14:43 ` Tudor Ambarus
2023-12-08 17:06 ` Mahapatra, Amit Kumar
2023-12-11 3:44 ` Tudor Ambarus
2023-12-08 17:05 ` Mahapatra, Amit Kumar
2023-12-11 3:33 ` Tudor Ambarus
2023-12-11 6:56 ` Mahapatra, Amit Kumar
2023-12-11 9:35 ` Tudor Ambarus
2023-12-11 13:37 ` Mahapatra, Amit Kumar
2023-12-12 15:02 ` Tudor Ambarus
2023-12-15 7:55 ` Mahapatra, Amit Kumar
2023-12-15 8:09 ` Tudor Ambarus
2023-12-15 10:02 ` Mahapatra, Amit Kumar
2023-12-15 10:33 ` Tudor Ambarus
2023-12-15 11:20 ` Mahapatra, Amit Kumar
2023-12-19 8:26 ` Tudor Ambarus
2023-12-21 6:54 ` Mahapatra, Amit Kumar
2024-02-09 11:06 ` Tudor Ambarus
2024-02-09 16:13 ` Tudor Ambarus
2024-03-13 16:03 ` Mahapatra, Amit Kumar
2024-07-26 12:35 ` Mahapatra, Amit Kumar
[not found] ` < <IA0PR12MB769944254171C39FF4171B52DCB42@IA0PR12MB7699.namprd12.prod.outlook.com>
2024-07-26 12:55 ` Michael Walle
2024-07-31 8:58 ` Michal Simek
2024-07-31 9:19 ` Michael Walle
2024-07-31 13:40 ` Michal Simek
2024-07-31 14:11 ` Michael Walle
2024-08-01 6:22 ` Michal Simek
2024-08-01 6:37 ` Frager, Neal
2024-08-01 9:28 ` Mahapatra, Amit Kumar
[not found] ` < <CH2PR12MB50044242FE253D7B0E3425ABF0B22@CH2PR12MB5004.namprd12.prod.outlook.com>
2024-08-05 8:14 ` Michael Walle
2024-08-05 8:27 ` Michael Walle
2024-08-05 11:00 ` Michal Simek
2024-08-07 13:21 ` Mahapatra, Amit Kumar
2024-08-12 7:29 ` Miquel Raynal
2024-08-12 7:37 ` Michael Walle
2024-08-12 8:39 ` Miquel Raynal
2024-08-12 8:38 ` Miquel Raynal
2024-08-12 9:45 ` Tudor Ambarus
2024-08-14 7:13 ` Mahapatra, Amit Kumar
2024-08-14 8:46 ` Miquel Raynal
2024-08-14 12:53 ` Mahapatra, Amit Kumar
2024-08-14 14:46 ` Miquel Raynal
2024-08-19 10:28 ` Mahapatra, Amit Kumar
2024-03-13 16:03 ` Mahapatra, Amit Kumar
2023-12-07 17:24 ` Tudor Ambarus
2023-11-25 9:21 ` [PATCH v11 08/10] spi: spi-zynqmp-gqspi: Add stacked memories support in GQSPI driver Amit Kumar Mahapatra
2023-11-25 9:21 ` Amit Kumar Mahapatra [this message]
2023-11-25 9:21 ` [PATCH v11 10/10] spi: spi-zynqmp-gqspi: Add parallel " Amit Kumar Mahapatra
2023-12-07 22:35 ` (subset) [PATCH v11 00/10] spi: Add support for stacked/parallel memories Mark Brown
2023-12-12 12:34 ` Michael Walle
2023-12-15 7:28 ` Mahapatra, Amit Kumar
2023-12-18 22:10 ` Richard Weinberger
2023-12-19 8:12 ` Miquel Raynal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231125092137.2948-10-amit.kumar-mahapatra@amd.com \
--to=amit.kumar-mahapatra@amd.com \
--cc=alexandre.belloni@bootlin.com \
--cc=alsa-devel@alsa-project.org \
--cc=amitrkcian2002@gmail.com \
--cc=broonie@kernel.org \
--cc=claudiu.beznea@tuxon.dev \
--cc=david.rhodes@cirrus.com \
--cc=git@amd.com \
--cc=james.schulman@cirrus.com \
--cc=lee@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=linux-sound@vger.kernel.org \
--cc=linux-spi@vger.kernel.org \
--cc=michael@walle.cc \
--cc=michal.simek@amd.com \
--cc=miquel.raynal@bootlin.com \
--cc=nicolas.ferre@microchip.com \
--cc=patches@opensource.cirrus.com \
--cc=perex@perex.cz \
--cc=pratyush@kernel.org \
--cc=rf@opensource.cirrus.com \
--cc=richard@nod.at \
--cc=sbinding@opensource.cirrus.com \
--cc=tiwai@suse.com \
--cc=tudor.ambarus@linaro.org \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).