All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michal Simek <michal.simek@amd.com>
To: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>,
	<broonie@kernel.org>, <miquel.raynal@bootlin.com>,
	<richard@nod.at>, <vigneshr@ti.com>, <jic23@kernel.org>,
	<tudor.ambarus@microchip.com>, <pratyush@kernel.org>,
	<sanju.mehta@amd.com>, <chin-ting_kuo@aspeedtech.com>,
	<clg@kaod.org>, <kdasu.kdev@gmail.com>, <f.fainelli@gmail.com>,
	<rjui@broadcom.com>, <sbranden@broadcom.com>,
	<eajames@linux.ibm.com>, <olteanv@gmail.com>, <han.xu@nxp.com>,
	<john.garry@huawei.com>, <shawnguo@kernel.org>,
	<s.hauer@pengutronix.de>, <narmstrong@baylibre.com>,
	<khilman@baylibre.com>, <matthias.bgg@gmail.com>,
	<haibo.chen@nxp.com>, <linus.walleij@linaro.org>,
	<daniel@zonque.org>, <haojian.zhuang@gmail.com>,
	<robert.jarzmik@free.fr>, <agross@kernel.org>,
	<bjorn.andersson@linaro.org>, <heiko@sntech.de>,
	<krzysztof.kozlowski@linaro.org>, <andi@etezian.org>,
	<mcoquelin.stm32@gmail.com>, <alexandre.torgue@foss.st.com>,
	<wens@csie.org>, <jernej.skrabec@gmail.com>,
	<samuel@sholland.org>, <masahisa.kojima@linaro.org>,
	<jaswinder.singh@linaro.org>, <rostedt@goodmis.org>,
	<mingo@redhat.com>, <l.stelmach@samsung.com>,
	<davem@davemloft.net>, <edumazet@google.com>, <kuba@kernel.org>,
	<pabeni@redhat.com>, <alex.aring@gmail.com>,
	<stefan@datenfreihafen.org>, <kvalo@kernel.org>,
	<thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
	<skomatineni@nvidia.com>, <sumit.semwal@linaro.org>,
	<christian.koenig@amd.com>, <j.neuschaefer@gmx.net>,
	<vireshk@kernel.org>, <rmfrfs@gmail.com>, <johan@kernel.org>,
	<elder@kernel.org>, <gregkh@linuxfoundation.org>
Cc: <git@amd.com>, <linux-spi@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <joel@jms.id.au>,
	<andrew@aj.id.au>, <radu_nicolae.pirea@upb.ro>,
	<nicolas.ferre@microchip.com>, <alexandre.belloni@bootlin.com>,
	<claudiu.beznea@microchip.com>,
	<bcm-kernel-feedback-list@broadcom.com>,
	<fancer.lancer@gmail.com>, <kernel@pengutronix.de>,
	<festevam@gmail.com>, <linux-imx@nxp.com>, <jbrunet@baylibre.com>,
	<martin.blumenstingl@googlemail.com>, <avifishman70@gmail.com>,
	<tmaimon77@gmail.com>, <tali.perry1@gmail.com>,
	<venture@google.com>, <yuenn@google.com>,
	<benjaminfair@google.com>, <yogeshgaur.83@gmail.com>,
	<konrad.dybcio@somainline.org>, <alim.akhtar@samsung.com>,
	<ldewangan@nvidia.com>, <linux-aspeed@lists.ozlabs.org>,
	<openbmc@lists.ozlabs.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-rpi-kernel@lists.infradead.org>,
	<linux-amlogic@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	<linux-arm-msm@vger.kernel.org>,
	<linux-rockchip@lists.infradead.org>,
	<linux-samsung-soc@vger.kernel.org>,
	<linux-stm32@st-md-mailman.stormreply.com>,
	<linux-sunxi@lists.linux.dev>, <linux-tegra@vger.kernel.org>,
	<netdev@vger.kernel.org>, <linux-wpan@vger.kernel.org>,
	<libertas-dev@lists.infradead.org>,
	<linux-wireless@vger.kernel.org>, <linux-mtd@lists.infradead.org>,
	<lars@metafoo.de>, <Michael.Hennerich@analog.com>,
	<linux-iio@vger.kernel.org>, <michael@walle.cc>,
	<palmer@dabbelt.com>, <linux-riscv@lists.infradead.org>,
	<linux-media@vger.kernel.org>, <dri-devel@lists.freedesktop.org>,
	<greybus-dev@lists.linaro.org>, <linux-staging@lists.linux.dev>,
	<amitrkcian2002@gmail.com>
Subject: Re: [PATCH v2 10/13] mtd: spi-nor: Add stacked memories support in spi-nor
Date: Mon, 23 Jan 2023 13:40:48 +0100	[thread overview]
Message-ID: <09534bb9-d9be-5433-5e7d-f9d40e30562e@amd.com> (raw)
In-Reply-To: <20230119185342.2093323-11-amit.kumar-mahapatra@amd.com>



On 1/19/23 19:53, Amit Kumar Mahapatra wrote:
> Each flash that is connected in stacked mode should have a separate
> parameter structure. So, the flash parameter member(*params) of the spi_nor
> structure is changed to an array (*params[2]). The array is used to store
> the parameters of each flash connected in stacked configuration.
> 
> The current implementation assumes that a maximum of two flashes are
> connected in stacked mode 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 is not aware of the chip_select values, for any incoming request
> SPI-NOR will decide the flash index with the help of individual flash size
> and the configuration type (single/stacked). SPI-NOR will pass on the flash
> index information to the SPI core & SPI driver by setting the appropriate
> bit in nor->spimem->spi->cs_index_mask. For example, if nth bit of
> nor->spimem->spi->cs_index_mask is set then the driver would
> assert/de-assert spi->chip_slect[n].
> 
> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> ---
>   drivers/mtd/spi-nor/core.c  | 282 +++++++++++++++++++++++++++++-------
>   drivers/mtd/spi-nor/core.h  |   4 +
>   drivers/mtd/spi-nor/otp.c   |   4 +-
>   include/linux/mtd/spi-nor.h |  12 +-
>   4 files changed, 246 insertions(+), 56 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 8a4a54bf2d0e..bb7326dc8b70 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1441,13 +1441,18 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
>   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;
> +	struct spi_nor_flash_parameter *params;
> +	u32 addr, len, offset, cur_cs_num = 0;
>   	uint32_t rem;
>   	int ret;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>   			(long long)instr->len);
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
>   	if (spi_nor_has_uniform_erase(nor)) {
>   		div_u64_rem(instr->len, mtd->erasesize, &rem);
>   		if (rem)
> @@ -1465,26 +1470,30 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
>   		unsigned long timeout;
>   
> -		ret = spi_nor_write_enable(nor);
> -		if (ret)
> -			goto erase_err;
> +		while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
> +			ret = spi_nor_write_enable(nor);
> +			if (ret)
> +				goto erase_err;
>   
> -		ret = spi_nor_erase_chip(nor);
> -		if (ret)
> -			goto erase_err;
> +			ret = spi_nor_erase_chip(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)(mtd->size / SZ_2M));
> -		ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
> -		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++;
> +		}
>   
>   	/* 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
> @@ -1493,12 +1502,26 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   	/* "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 < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   		while (len) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
>   			ret = spi_nor_write_enable(nor);
>   			if (ret)
>   				goto erase_err;
>   
> -			ret = spi_nor_erase_sector(nor, addr);
> +			offset = addr;
> +			if (nor->flags & SNOR_F_HAS_STACKED) {
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				offset -= (sz - params->size);
> +			}
> +
> +			ret = spi_nor_erase_sector(nor, offset);
>   			if (ret)
>   				goto erase_err;
>   
> @@ -1508,13 +1531,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   			addr += mtd->erasesize;
>   			len -= mtd->erasesize;
> +
> +			/*
> +			 * Flash cross over condition in stacked mode.
> +			 */
> +			if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) {
> +				cur_cs_num++;
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				sz += params->size;
> +			}
>   		}
>   
>   	/* erase multiple sectors */
>   	} else {
> -		ret = spi_nor_erase_multi_sectors(nor, addr, len);
> -		if (ret)
> -			goto erase_err;
> +		u64 erase_len = 0;
> +
> +		/* Determine the flash from which the operation need to start */
> +		while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			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);
> +			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;
> +		}
>   	}
>   
>   	ret = spi_nor_write_disable(nor);
> @@ -1713,7 +1768,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   			size_t *retlen, u_char *buf)
>   {
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
> -	ssize_t ret;
> +	struct spi_nor_flash_parameter *params;
> +	ssize_t ret, read_len;
> +	u32 cur_cs_num = 0;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>   
> @@ -1721,9 +1779,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   	if (ret)
>   		return ret;
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) {
> +		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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_read_data(nor, addr, len, buf);
> @@ -1735,11 +1807,22 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   		if (ret < 0)
>   			goto read_err;
>   
> -		WARN_ON(ret > len);
> +		WARN_ON(ret > read_len);
>   		*retlen += ret;
>   		buf += ret;
>   		from += ret;
>   		len -= ret;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 *
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && (from > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   	}
>   	ret = 0;
>   
> @@ -1759,13 +1842,22 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
>   	struct spi_nor_flash_parameter *params;
>   	size_t page_offset, page_remain, i;
> +	u32 page_size, cur_cs_num = 0;
>   	ssize_t ret;
> -	u32 page_size;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>   
>   	params = spi_nor_get_params(nor, 0);
>   	page_size = params->page_size;
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) {
> +		cur_cs_num++;
> +		params = spi_nor_get_params(nor, cur_cs_num);
> +		sz += params->size;
> +	}
>   
>   	ret = spi_nor_lock_and_prep(nor);
>   	if (ret)
> @@ -1790,6 +1882,10 @@ 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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_write_enable(nor);
> @@ -1806,6 +1902,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   			goto write_err;
>   		*retlen += written;
>   		i += written;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && ((to + i) > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
>   	}
>   
>   write_err:
> @@ -1918,8 +2023,6 @@ int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
>   static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   				   struct spi_mem_op *op)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -
>   	/*
>   	 * First test with 4 address bytes. The opcode itself might
>   	 * be a 3B addressing opcode but we don't care, because
> @@ -1928,7 +2031,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   	 */
>   	op->addr.nbytes = 4;
>   	if (!spi_mem_supports_op(nor->spimem, op)) {
> -		if (params->size > SZ_16M)
> +		if (nor->mtd.size > SZ_16M)
>   			return -EOPNOTSUPP;
>   
>   		/* If flash size <= 16MB, 3 address bytes are sufficient */
> @@ -2516,6 +2619,10 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
>   static void spi_nor_late_init_params(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct device_node *np = spi_nor_get_flash_node(nor);
> +	u64 flash_size[SNOR_FLASH_CNT_MAX];
> +	u32 idx = 0, i = 0;
> +	int rc;
>   
>   	if (nor->manufacturer && nor->manufacturer->fixups &&
>   	    nor->manufacturer->fixups->late_init)
> @@ -2533,6 +2640,36 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
>   	 */
>   	if (nor->flags & SNOR_F_HAS_LOCK && !params->locking_ops)
>   		spi_nor_init_default_locking_ops(nor);
> +	/*
> +	 * 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 = of_property_read_u64_index(np, "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->flags & SNOR_F_HAS_STACKED) {
> +		for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
> +			if (params) {
> +				memcpy(params, spi_nor_get_params(nor, 0), sizeof(*params));
> +				params->size = flash_size[idx];
> +				spi_nor_set_params(nor, idx, params);
> +			}
> +		}
> +	}
>   }
>   
>   /**
> @@ -2741,22 +2878,36 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
>    */
>   static int spi_nor_quad_enable(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
> -	if (!params->quad_enable)
> -		return 0;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params) {
> +			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;
> +			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 = 0x01 << idx;
>   
> -	return params->quad_enable(nor);
> +			err = params->quad_enable(nor);
> +			if (err)
> +				return err;
> +		}
> +	}
> +	return err;
>   }
>   
>   static int spi_nor_init(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -	int err;
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
>   	err = spi_nor_octal_dtr_enable(nor, true);
>   	if (err) {
> @@ -2797,9 +2948,19 @@ 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");
> -		err = params->set_4byte_addr_mode(nor, true);
> -		if (err && err != -ENOTSUPP)
> -			return err;
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				err = params->set_4byte_addr_mode(nor, true);
> +				if (err && err != -ENOTSUPP)
> +					return err;
> +			}
> +		}
>   	}
>   
>   	return 0;
> @@ -2915,19 +3076,31 @@ void spi_nor_restore(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params;
>   	int ret;
> +	int idx;
>   
>   	/* restore the addressing mode */
>   	if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
>   	    nor->flags & SNOR_F_BROKEN_RESET) {
> -		params = spi_nor_get_params(nor, 0);
> -		ret = params->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);
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				ret = params->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);
> +			}
> +		}
>   	}
>   
>   	if (nor->flags & SNOR_F_SOFT_RESET)
> @@ -2995,6 +3168,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
>   	struct mtd_info *mtd = &nor->mtd;
>   	struct device *dev = nor->dev;
> +	u64 total_sz = 0;
> +	int idx;
>   
>   	spi_nor_set_mtd_locking_ops(nor);
>   	spi_nor_set_mtd_otp_ops(nor);
> @@ -3010,7 +3185,12 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   		mtd->_erase = spi_nor_erase;
>   	mtd->writesize = params->writesize;
>   	mtd->writebufsize = params->page_size;
> -	mtd->size = params->size;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params)
> +			total_sz += params->size;
> +	}
> +	mtd->size = total_sz;
>   	mtd->_read = spi_nor_read;
>   	/* Might be already set by some SST flashes. */
>   	if (!mtd->_write)
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index f03b55cf7e6f..e94107cc465e 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -11,6 +11,9 @@
>   
>   #define SPI_NOR_MAX_ID_LEN	6
>   
> +/* In single configuration enable CS0 */
> +#define SPI_NOR_ENABLE_CS0     BIT(0)
> +
>   /* 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),			\
> @@ -130,6 +133,7 @@ enum spi_nor_option_flags {
>   	SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
>   	SNOR_F_SOFT_RESET	= BIT(12),
>   	SNOR_F_SWP_IS_VOLATILE	= BIT(13),
> +	SNOR_F_HAS_STACKED      = BIT(14),
>   };
>   
>   struct spi_nor_read_command {
> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
> index a9c0844d55ef..b8c7e085a90d 100644
> --- a/drivers/mtd/spi-nor/otp.c
> +++ b/drivers/mtd/spi-nor/otp.c
> @@ -11,8 +11,8 @@
>   
>   #include "core.h"
>   
> -#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
> -#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
> +#define spi_nor_otp_region_len(nor) ((nor)->params[0]->otp.org->len)
> +#define spi_nor_otp_n_regions(nor) ((nor)->params[0]->otp.org->n_regions)

I think this should be also converted to static inline functions and use
spi_nor_get_params(nor, 0); instead of using arrays.
Can be done on the top of this series if it is the only one problem with it.

Thanks,
Michal

WARNING: multiple messages have this Message-ID (diff)
From: Michal Simek <michal.simek@amd.com>
To: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>,
	<broonie@kernel.org>, <miquel.raynal@bootlin.com>,
	<richard@nod.at>, <vigneshr@ti.com>, <jic23@kernel.org>,
	<tudor.ambarus@microchip.com>, <pratyush@kernel.org>,
	<sanju.mehta@amd.com>, <chin-ting_kuo@aspeedtech.com>,
	<clg@kaod.org>, <kdasu.kdev@gmail.com>, <f.fainelli@gmail.com>,
	<rjui@broadcom.com>, <sbranden@broadcom.com>,
	<eajames@linux.ibm.com>, <olteanv@gmail.com>, <han.xu@nxp.com>,
	<john.garry@huawei.com>, <shawnguo@kernel.org>,
	<s.hauer@pengutronix.de>, <narmstrong@baylibre.com>,
	<khilman@baylibre.com>, <matthias.bgg@gmail.com>,
	<haibo.chen@nxp.com>, <linus.walleij@linaro.org>,
	<daniel@zonque.org>, <haojian.zhuang@gmail.com>,
	<robert.jarzmik@free.fr>, <agross@kernel.org>,
	<bjorn.andersson@linaro.org>, <heiko@sntech.de>,
	<krzysztof.kozlowski@linaro.org>, <andi@etezian.org>,
	<mcoquelin.stm32@gmail.com>, <alexandre.torgue@foss.st.com>,
	<wens@csie.org>, <jernej.skrabec@gmail.com>,
	<samuel@sholland.org>, <masahisa.kojima@linaro.org>,
	<jaswinder.singh@linaro.org>, <rostedt@goodmis.org>,
	<mingo@redhat.com>, <l.stelmach@samsung.com>,
	<davem@davemloft.net>, <edumazet@google.com>, <kuba@kernel.org>,
	<pabeni@redhat.com>, <alex.aring@gmail.com>,
	<stefan@datenfreihafen.org>, <kvalo@kernel.org>,
	<thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
	<skomatineni@nvidia.com>, <sumit.semwal@linaro.org>,
	<christian.koenig@amd.com>, <j.neuschaefer@gmx.net>,
	<vireshk@kernel.org>, <rmfrfs@gmail.com>, <johan@kernel.org>,
	<elder@kernel.org>, <gregkh@linuxfoundation.org>
Cc: <git@amd.com>, <linux-spi@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <joel@jms.id.au>,
	<andrew@aj.id.au>, <radu_nicolae.pirea@upb.ro>,
	<nicolas.ferre@microchip.com>, <alexandre.belloni@bootlin.com>,
	<claudiu.beznea@microchip.com>,
	<bcm-kernel-feedback-list@broadcom.com>,
	<fancer.lancer@gmail.com>, <kernel@pengutronix.de>,
	<festevam@gmail.com>, <linux-imx@nxp.com>, <jbrunet@baylibre.com>,
	<martin.blumenstingl@googlemail.com>, <avifishman70@gmail.com>,
	<tmaimon77@gmail.com>, <tali.perry1@gmail.com>,
	<venture@google.com>, <yuenn@google.com>,
	<benjaminfair@google.com>, <yogeshgaur.83@gmail.com>,
	<konrad.dybcio@somainline.org>, <alim.akhtar@samsung.com>,
	<ldewangan@nvidia.com>, <linux-aspeed@lists.ozlabs.org>,
	<openbmc@lists.ozlabs.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-rpi-kernel@lists.infradead.org>,
	<linux-amlogic@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	<linux-arm-msm@vger.kernel.org>,
	<linux-rockchip@lists.infradead.org>,
	<linux-samsung-soc@vger.kernel.org>,
	<linux-stm32@st-md-mailman.stormreply.com>,
	<linux-sunxi@lists.linux.dev>, <linux-tegra@vger.kernel.org>,
	<netdev@vger.kernel.org>, <linux-wpan@vger.kernel.org>,
	<libertas-dev@lists.infradead.org>,
	<linux-wireless@vger.kernel.org>, <linux-mtd@lists.infradead.org>,
	<lars@metafoo.de>, <Michael.Hennerich@analog.com>,
	<linux-iio@vger.kernel.org>, <michael@walle.cc>,
	<palmer@dabbelt.com>, <linux-riscv@lists.infradead.org>,
	<linux-media@vger.kernel.org>, <dri-devel@lists.freedesktop.org>,
	<greybus-dev@lists.linaro.org>, <linux-staging@lists.linux.dev>,
	<amitrkcian2002@gmail.com>
Subject: Re: [PATCH v2 10/13] mtd: spi-nor: Add stacked memories support in spi-nor
Date: Mon, 23 Jan 2023 13:40:48 +0100	[thread overview]
Message-ID: <09534bb9-d9be-5433-5e7d-f9d40e30562e@amd.com> (raw)
In-Reply-To: <20230119185342.2093323-11-amit.kumar-mahapatra@amd.com>



On 1/19/23 19:53, Amit Kumar Mahapatra wrote:
> Each flash that is connected in stacked mode should have a separate
> parameter structure. So, the flash parameter member(*params) of the spi_nor
> structure is changed to an array (*params[2]). The array is used to store
> the parameters of each flash connected in stacked configuration.
> 
> The current implementation assumes that a maximum of two flashes are
> connected in stacked mode 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 is not aware of the chip_select values, for any incoming request
> SPI-NOR will decide the flash index with the help of individual flash size
> and the configuration type (single/stacked). SPI-NOR will pass on the flash
> index information to the SPI core & SPI driver by setting the appropriate
> bit in nor->spimem->spi->cs_index_mask. For example, if nth bit of
> nor->spimem->spi->cs_index_mask is set then the driver would
> assert/de-assert spi->chip_slect[n].
> 
> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> ---
>   drivers/mtd/spi-nor/core.c  | 282 +++++++++++++++++++++++++++++-------
>   drivers/mtd/spi-nor/core.h  |   4 +
>   drivers/mtd/spi-nor/otp.c   |   4 +-
>   include/linux/mtd/spi-nor.h |  12 +-
>   4 files changed, 246 insertions(+), 56 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 8a4a54bf2d0e..bb7326dc8b70 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1441,13 +1441,18 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
>   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;
> +	struct spi_nor_flash_parameter *params;
> +	u32 addr, len, offset, cur_cs_num = 0;
>   	uint32_t rem;
>   	int ret;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>   			(long long)instr->len);
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
>   	if (spi_nor_has_uniform_erase(nor)) {
>   		div_u64_rem(instr->len, mtd->erasesize, &rem);
>   		if (rem)
> @@ -1465,26 +1470,30 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
>   		unsigned long timeout;
>   
> -		ret = spi_nor_write_enable(nor);
> -		if (ret)
> -			goto erase_err;
> +		while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
> +			ret = spi_nor_write_enable(nor);
> +			if (ret)
> +				goto erase_err;
>   
> -		ret = spi_nor_erase_chip(nor);
> -		if (ret)
> -			goto erase_err;
> +			ret = spi_nor_erase_chip(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)(mtd->size / SZ_2M));
> -		ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
> -		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++;
> +		}
>   
>   	/* 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
> @@ -1493,12 +1502,26 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   	/* "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 < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   		while (len) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
>   			ret = spi_nor_write_enable(nor);
>   			if (ret)
>   				goto erase_err;
>   
> -			ret = spi_nor_erase_sector(nor, addr);
> +			offset = addr;
> +			if (nor->flags & SNOR_F_HAS_STACKED) {
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				offset -= (sz - params->size);
> +			}
> +
> +			ret = spi_nor_erase_sector(nor, offset);
>   			if (ret)
>   				goto erase_err;
>   
> @@ -1508,13 +1531,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   			addr += mtd->erasesize;
>   			len -= mtd->erasesize;
> +
> +			/*
> +			 * Flash cross over condition in stacked mode.
> +			 */
> +			if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) {
> +				cur_cs_num++;
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				sz += params->size;
> +			}
>   		}
>   
>   	/* erase multiple sectors */
>   	} else {
> -		ret = spi_nor_erase_multi_sectors(nor, addr, len);
> -		if (ret)
> -			goto erase_err;
> +		u64 erase_len = 0;
> +
> +		/* Determine the flash from which the operation need to start */
> +		while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			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);
> +			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;
> +		}
>   	}
>   
>   	ret = spi_nor_write_disable(nor);
> @@ -1713,7 +1768,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   			size_t *retlen, u_char *buf)
>   {
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
> -	ssize_t ret;
> +	struct spi_nor_flash_parameter *params;
> +	ssize_t ret, read_len;
> +	u32 cur_cs_num = 0;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>   
> @@ -1721,9 +1779,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   	if (ret)
>   		return ret;
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) {
> +		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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_read_data(nor, addr, len, buf);
> @@ -1735,11 +1807,22 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   		if (ret < 0)
>   			goto read_err;
>   
> -		WARN_ON(ret > len);
> +		WARN_ON(ret > read_len);
>   		*retlen += ret;
>   		buf += ret;
>   		from += ret;
>   		len -= ret;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 *
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && (from > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   	}
>   	ret = 0;
>   
> @@ -1759,13 +1842,22 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
>   	struct spi_nor_flash_parameter *params;
>   	size_t page_offset, page_remain, i;
> +	u32 page_size, cur_cs_num = 0;
>   	ssize_t ret;
> -	u32 page_size;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>   
>   	params = spi_nor_get_params(nor, 0);
>   	page_size = params->page_size;
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) {
> +		cur_cs_num++;
> +		params = spi_nor_get_params(nor, cur_cs_num);
> +		sz += params->size;
> +	}
>   
>   	ret = spi_nor_lock_and_prep(nor);
>   	if (ret)
> @@ -1790,6 +1882,10 @@ 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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_write_enable(nor);
> @@ -1806,6 +1902,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   			goto write_err;
>   		*retlen += written;
>   		i += written;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && ((to + i) > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
>   	}
>   
>   write_err:
> @@ -1918,8 +2023,6 @@ int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
>   static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   				   struct spi_mem_op *op)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -
>   	/*
>   	 * First test with 4 address bytes. The opcode itself might
>   	 * be a 3B addressing opcode but we don't care, because
> @@ -1928,7 +2031,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   	 */
>   	op->addr.nbytes = 4;
>   	if (!spi_mem_supports_op(nor->spimem, op)) {
> -		if (params->size > SZ_16M)
> +		if (nor->mtd.size > SZ_16M)
>   			return -EOPNOTSUPP;
>   
>   		/* If flash size <= 16MB, 3 address bytes are sufficient */
> @@ -2516,6 +2619,10 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
>   static void spi_nor_late_init_params(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct device_node *np = spi_nor_get_flash_node(nor);
> +	u64 flash_size[SNOR_FLASH_CNT_MAX];
> +	u32 idx = 0, i = 0;
> +	int rc;
>   
>   	if (nor->manufacturer && nor->manufacturer->fixups &&
>   	    nor->manufacturer->fixups->late_init)
> @@ -2533,6 +2640,36 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
>   	 */
>   	if (nor->flags & SNOR_F_HAS_LOCK && !params->locking_ops)
>   		spi_nor_init_default_locking_ops(nor);
> +	/*
> +	 * 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 = of_property_read_u64_index(np, "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->flags & SNOR_F_HAS_STACKED) {
> +		for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
> +			if (params) {
> +				memcpy(params, spi_nor_get_params(nor, 0), sizeof(*params));
> +				params->size = flash_size[idx];
> +				spi_nor_set_params(nor, idx, params);
> +			}
> +		}
> +	}
>   }
>   
>   /**
> @@ -2741,22 +2878,36 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
>    */
>   static int spi_nor_quad_enable(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
> -	if (!params->quad_enable)
> -		return 0;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params) {
> +			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;
> +			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 = 0x01 << idx;
>   
> -	return params->quad_enable(nor);
> +			err = params->quad_enable(nor);
> +			if (err)
> +				return err;
> +		}
> +	}
> +	return err;
>   }
>   
>   static int spi_nor_init(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -	int err;
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
>   	err = spi_nor_octal_dtr_enable(nor, true);
>   	if (err) {
> @@ -2797,9 +2948,19 @@ 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");
> -		err = params->set_4byte_addr_mode(nor, true);
> -		if (err && err != -ENOTSUPP)
> -			return err;
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				err = params->set_4byte_addr_mode(nor, true);
> +				if (err && err != -ENOTSUPP)
> +					return err;
> +			}
> +		}
>   	}
>   
>   	return 0;
> @@ -2915,19 +3076,31 @@ void spi_nor_restore(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params;
>   	int ret;
> +	int idx;
>   
>   	/* restore the addressing mode */
>   	if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
>   	    nor->flags & SNOR_F_BROKEN_RESET) {
> -		params = spi_nor_get_params(nor, 0);
> -		ret = params->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);
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				ret = params->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);
> +			}
> +		}
>   	}
>   
>   	if (nor->flags & SNOR_F_SOFT_RESET)
> @@ -2995,6 +3168,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
>   	struct mtd_info *mtd = &nor->mtd;
>   	struct device *dev = nor->dev;
> +	u64 total_sz = 0;
> +	int idx;
>   
>   	spi_nor_set_mtd_locking_ops(nor);
>   	spi_nor_set_mtd_otp_ops(nor);
> @@ -3010,7 +3185,12 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   		mtd->_erase = spi_nor_erase;
>   	mtd->writesize = params->writesize;
>   	mtd->writebufsize = params->page_size;
> -	mtd->size = params->size;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params)
> +			total_sz += params->size;
> +	}
> +	mtd->size = total_sz;
>   	mtd->_read = spi_nor_read;
>   	/* Might be already set by some SST flashes. */
>   	if (!mtd->_write)
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index f03b55cf7e6f..e94107cc465e 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -11,6 +11,9 @@
>   
>   #define SPI_NOR_MAX_ID_LEN	6
>   
> +/* In single configuration enable CS0 */
> +#define SPI_NOR_ENABLE_CS0     BIT(0)
> +
>   /* 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),			\
> @@ -130,6 +133,7 @@ enum spi_nor_option_flags {
>   	SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
>   	SNOR_F_SOFT_RESET	= BIT(12),
>   	SNOR_F_SWP_IS_VOLATILE	= BIT(13),
> +	SNOR_F_HAS_STACKED      = BIT(14),
>   };
>   
>   struct spi_nor_read_command {
> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
> index a9c0844d55ef..b8c7e085a90d 100644
> --- a/drivers/mtd/spi-nor/otp.c
> +++ b/drivers/mtd/spi-nor/otp.c
> @@ -11,8 +11,8 @@
>   
>   #include "core.h"
>   
> -#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
> -#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
> +#define spi_nor_otp_region_len(nor) ((nor)->params[0]->otp.org->len)
> +#define spi_nor_otp_n_regions(nor) ((nor)->params[0]->otp.org->n_regions)

I think this should be also converted to static inline functions and use
spi_nor_get_params(nor, 0); instead of using arrays.
Can be done on the top of this series if it is the only one problem with it.

Thanks,
Michal

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

WARNING: multiple messages have this Message-ID (diff)
From: Michal Simek <michal.simek@amd.com>
To: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>,
	<broonie@kernel.org>,  <miquel.raynal@bootlin.com>,
	<richard@nod.at>, <vigneshr@ti.com>, <jic23@kernel.org>,
	<tudor.ambarus@microchip.com>, <pratyush@kernel.org>,
	<sanju.mehta@amd.com>, <chin-ting_kuo@aspeedtech.com>,
	<clg@kaod.org>, <kdasu.kdev@gmail.com>, <f.fainelli@gmail.com>,
	<rjui@broadcom.com>, <sbranden@broadcom.com>,
	<eajames@linux.ibm.com>, <olteanv@gmail.com>, <han.xu@nxp.com>,
	<john.garry@huawei.com>, <shawnguo@kernel.org>,
	<s.hauer@pengutronix.de>, <narmstrong@baylibre.com>,
	<khilman@baylibre.com>, <matthias.bgg@gmail.com>,
	<haibo.chen@nxp.com>, <linus.walleij@linaro.org>,
	<daniel@zonque.org>, <haojian.zhuang@gmail.com>,
	<robert.jarzmik@free.fr>, <agross@kernel.org>,
	<bjorn.andersson@linaro.org>, <heiko@sntech.de>,
	<krzysztof.kozlowski@linaro.org>, <andi@etezian.org>,
	<mcoquelin.stm32@gmail.com>, <alexandre.torgue@foss.st.com>,
	<wens@csie.org>,  <jernej.skrabec@gmail.com>,
	<samuel@sholland.org>, <masahisa.kojima@linaro.org>,
	<jaswinder.singh@linaro.org>, <rostedt@goodmis.org>,
	<mingo@redhat.com>, <l.stelmach@samsung.com>,
	<davem@davemloft.net>, <edumazet@google.com>, <kuba@kernel.org>,
	<pabeni@redhat.com>, <alex.aring@gmail.com>,
	<stefan@datenfreihafen.org>, <kvalo@kernel.org>,
	<thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
	<skomatineni@nvidia.com>, <sumit.semwal@linaro.org>,
	<christian.koenig@amd.com>, <j.neuschaefer@gmx.net>,
	<vireshk@kernel.org>, <rmfrfs@gmail.com>, <johan@kernel.org>,
	<elder@kernel.org>, <gregkh@linuxfoundation.org>
Cc: alexandre.belloni@bootlin.com, tmaimon77@gmail.com,
	linux-aspeed@lists.ozlabs.org, linux-iio@vger.kernel.org,
	konrad.dybcio@somainline.org, dri-devel@lists.freedesktop.org,
	tali.perry1@gmail.com, ldewangan@nvidia.com,
	linux-mtd@lists.infradead.org, alim.akhtar@samsung.com,
	linux-riscv@lists.infradead.org, linux-spi@vger.kernel.org,
	linux-stm32@st-md-mailman.stormreply.com, jbrunet@baylibre.com,
	git@amd.com, linux-samsung-soc@vger.kernel.org,
	benjaminfair@google.com, yogeshgaur.83@gmail.com,
	openbmc@lists.ozlabs.org, linux-staging@lists.linux.dev,
	yuenn@google.com, bcm-kernel-feedback-list@broadcom.com,
	joel@jms.id.au, linux-rockchip@lists.infradead.org,
	linux-sunxi@lists.linux.dev, linux-imx@nxp.com,
	amitrkcian2002@gmail.com, Michael.Hennerich@analog.com,
	martin.blumenstingl@googlemail.com,
	linux-arm-msm@vger.kernel.org, radu_nicolae.pirea@upb.ro,
	greybus-dev@lists.linaro.org, linux-mediatek@lists.infradead.org,
	linux-rpi-kernel@lists.infradead.org,
	linux-tegra@vger.kernel.org, linux-amlogic@lists.infradead.org,
	linux-arm-kernel@lists.infradead.org, avifishman70@gmail.com,
	venture@google.com, libertas-dev@lists.infradead.org,
	linux-wireless@vger.kernel.org, nicolas.ferre@microchip.com,
	fancer.lancer@gmail.com, linux-kernel@vger.kernel.org,
	andrew@aj.id.au, michael@walle.cc, palmer@dabbelt.com,
	kernel@pengutronix.de, netdev@vger.kernel.org,
	linux-media@vger.kernel.org, linux-wpan@vger.kernel.org,
	claudiu.beznea@microchip.com
Subject: Re: [PATCH v2 10/13] mtd: spi-nor: Add stacked memories support in spi-nor
Date: Mon, 23 Jan 2023 13:40:48 +0100	[thread overview]
Message-ID: <09534bb9-d9be-5433-5e7d-f9d40e30562e@amd.com> (raw)
In-Reply-To: <20230119185342.2093323-11-amit.kumar-mahapatra@amd.com>



On 1/19/23 19:53, Amit Kumar Mahapatra wrote:
> Each flash that is connected in stacked mode should have a separate
> parameter structure. So, the flash parameter member(*params) of the spi_nor
> structure is changed to an array (*params[2]). The array is used to store
> the parameters of each flash connected in stacked configuration.
> 
> The current implementation assumes that a maximum of two flashes are
> connected in stacked mode 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 is not aware of the chip_select values, for any incoming request
> SPI-NOR will decide the flash index with the help of individual flash size
> and the configuration type (single/stacked). SPI-NOR will pass on the flash
> index information to the SPI core & SPI driver by setting the appropriate
> bit in nor->spimem->spi->cs_index_mask. For example, if nth bit of
> nor->spimem->spi->cs_index_mask is set then the driver would
> assert/de-assert spi->chip_slect[n].
> 
> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> ---
>   drivers/mtd/spi-nor/core.c  | 282 +++++++++++++++++++++++++++++-------
>   drivers/mtd/spi-nor/core.h  |   4 +
>   drivers/mtd/spi-nor/otp.c   |   4 +-
>   include/linux/mtd/spi-nor.h |  12 +-
>   4 files changed, 246 insertions(+), 56 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 8a4a54bf2d0e..bb7326dc8b70 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1441,13 +1441,18 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
>   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;
> +	struct spi_nor_flash_parameter *params;
> +	u32 addr, len, offset, cur_cs_num = 0;
>   	uint32_t rem;
>   	int ret;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>   			(long long)instr->len);
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
>   	if (spi_nor_has_uniform_erase(nor)) {
>   		div_u64_rem(instr->len, mtd->erasesize, &rem);
>   		if (rem)
> @@ -1465,26 +1470,30 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
>   		unsigned long timeout;
>   
> -		ret = spi_nor_write_enable(nor);
> -		if (ret)
> -			goto erase_err;
> +		while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
> +			ret = spi_nor_write_enable(nor);
> +			if (ret)
> +				goto erase_err;
>   
> -		ret = spi_nor_erase_chip(nor);
> -		if (ret)
> -			goto erase_err;
> +			ret = spi_nor_erase_chip(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)(mtd->size / SZ_2M));
> -		ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
> -		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++;
> +		}
>   
>   	/* 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
> @@ -1493,12 +1502,26 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   	/* "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 < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   		while (len) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
>   			ret = spi_nor_write_enable(nor);
>   			if (ret)
>   				goto erase_err;
>   
> -			ret = spi_nor_erase_sector(nor, addr);
> +			offset = addr;
> +			if (nor->flags & SNOR_F_HAS_STACKED) {
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				offset -= (sz - params->size);
> +			}
> +
> +			ret = spi_nor_erase_sector(nor, offset);
>   			if (ret)
>   				goto erase_err;
>   
> @@ -1508,13 +1531,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   			addr += mtd->erasesize;
>   			len -= mtd->erasesize;
> +
> +			/*
> +			 * Flash cross over condition in stacked mode.
> +			 */
> +			if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) {
> +				cur_cs_num++;
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				sz += params->size;
> +			}
>   		}
>   
>   	/* erase multiple sectors */
>   	} else {
> -		ret = spi_nor_erase_multi_sectors(nor, addr, len);
> -		if (ret)
> -			goto erase_err;
> +		u64 erase_len = 0;
> +
> +		/* Determine the flash from which the operation need to start */
> +		while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			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);
> +			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;
> +		}
>   	}
>   
>   	ret = spi_nor_write_disable(nor);
> @@ -1713,7 +1768,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   			size_t *retlen, u_char *buf)
>   {
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
> -	ssize_t ret;
> +	struct spi_nor_flash_parameter *params;
> +	ssize_t ret, read_len;
> +	u32 cur_cs_num = 0;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>   
> @@ -1721,9 +1779,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   	if (ret)
>   		return ret;
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) {
> +		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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_read_data(nor, addr, len, buf);
> @@ -1735,11 +1807,22 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   		if (ret < 0)
>   			goto read_err;
>   
> -		WARN_ON(ret > len);
> +		WARN_ON(ret > read_len);
>   		*retlen += ret;
>   		buf += ret;
>   		from += ret;
>   		len -= ret;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 *
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && (from > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   	}
>   	ret = 0;
>   
> @@ -1759,13 +1842,22 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
>   	struct spi_nor_flash_parameter *params;
>   	size_t page_offset, page_remain, i;
> +	u32 page_size, cur_cs_num = 0;
>   	ssize_t ret;
> -	u32 page_size;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>   
>   	params = spi_nor_get_params(nor, 0);
>   	page_size = params->page_size;
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) {
> +		cur_cs_num++;
> +		params = spi_nor_get_params(nor, cur_cs_num);
> +		sz += params->size;
> +	}
>   
>   	ret = spi_nor_lock_and_prep(nor);
>   	if (ret)
> @@ -1790,6 +1882,10 @@ 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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_write_enable(nor);
> @@ -1806,6 +1902,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   			goto write_err;
>   		*retlen += written;
>   		i += written;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && ((to + i) > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
>   	}
>   
>   write_err:
> @@ -1918,8 +2023,6 @@ int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
>   static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   				   struct spi_mem_op *op)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -
>   	/*
>   	 * First test with 4 address bytes. The opcode itself might
>   	 * be a 3B addressing opcode but we don't care, because
> @@ -1928,7 +2031,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   	 */
>   	op->addr.nbytes = 4;
>   	if (!spi_mem_supports_op(nor->spimem, op)) {
> -		if (params->size > SZ_16M)
> +		if (nor->mtd.size > SZ_16M)
>   			return -EOPNOTSUPP;
>   
>   		/* If flash size <= 16MB, 3 address bytes are sufficient */
> @@ -2516,6 +2619,10 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
>   static void spi_nor_late_init_params(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct device_node *np = spi_nor_get_flash_node(nor);
> +	u64 flash_size[SNOR_FLASH_CNT_MAX];
> +	u32 idx = 0, i = 0;
> +	int rc;
>   
>   	if (nor->manufacturer && nor->manufacturer->fixups &&
>   	    nor->manufacturer->fixups->late_init)
> @@ -2533,6 +2640,36 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
>   	 */
>   	if (nor->flags & SNOR_F_HAS_LOCK && !params->locking_ops)
>   		spi_nor_init_default_locking_ops(nor);
> +	/*
> +	 * 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 = of_property_read_u64_index(np, "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->flags & SNOR_F_HAS_STACKED) {
> +		for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
> +			if (params) {
> +				memcpy(params, spi_nor_get_params(nor, 0), sizeof(*params));
> +				params->size = flash_size[idx];
> +				spi_nor_set_params(nor, idx, params);
> +			}
> +		}
> +	}
>   }
>   
>   /**
> @@ -2741,22 +2878,36 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
>    */
>   static int spi_nor_quad_enable(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
> -	if (!params->quad_enable)
> -		return 0;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params) {
> +			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;
> +			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 = 0x01 << idx;
>   
> -	return params->quad_enable(nor);
> +			err = params->quad_enable(nor);
> +			if (err)
> +				return err;
> +		}
> +	}
> +	return err;
>   }
>   
>   static int spi_nor_init(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -	int err;
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
>   	err = spi_nor_octal_dtr_enable(nor, true);
>   	if (err) {
> @@ -2797,9 +2948,19 @@ 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");
> -		err = params->set_4byte_addr_mode(nor, true);
> -		if (err && err != -ENOTSUPP)
> -			return err;
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				err = params->set_4byte_addr_mode(nor, true);
> +				if (err && err != -ENOTSUPP)
> +					return err;
> +			}
> +		}
>   	}
>   
>   	return 0;
> @@ -2915,19 +3076,31 @@ void spi_nor_restore(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params;
>   	int ret;
> +	int idx;
>   
>   	/* restore the addressing mode */
>   	if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
>   	    nor->flags & SNOR_F_BROKEN_RESET) {
> -		params = spi_nor_get_params(nor, 0);
> -		ret = params->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);
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				ret = params->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);
> +			}
> +		}
>   	}
>   
>   	if (nor->flags & SNOR_F_SOFT_RESET)
> @@ -2995,6 +3168,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
>   	struct mtd_info *mtd = &nor->mtd;
>   	struct device *dev = nor->dev;
> +	u64 total_sz = 0;
> +	int idx;
>   
>   	spi_nor_set_mtd_locking_ops(nor);
>   	spi_nor_set_mtd_otp_ops(nor);
> @@ -3010,7 +3185,12 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   		mtd->_erase = spi_nor_erase;
>   	mtd->writesize = params->writesize;
>   	mtd->writebufsize = params->page_size;
> -	mtd->size = params->size;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params)
> +			total_sz += params->size;
> +	}
> +	mtd->size = total_sz;
>   	mtd->_read = spi_nor_read;
>   	/* Might be already set by some SST flashes. */
>   	if (!mtd->_write)
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index f03b55cf7e6f..e94107cc465e 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -11,6 +11,9 @@
>   
>   #define SPI_NOR_MAX_ID_LEN	6
>   
> +/* In single configuration enable CS0 */
> +#define SPI_NOR_ENABLE_CS0     BIT(0)
> +
>   /* 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),			\
> @@ -130,6 +133,7 @@ enum spi_nor_option_flags {
>   	SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
>   	SNOR_F_SOFT_RESET	= BIT(12),
>   	SNOR_F_SWP_IS_VOLATILE	= BIT(13),
> +	SNOR_F_HAS_STACKED      = BIT(14),
>   };
>   
>   struct spi_nor_read_command {
> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
> index a9c0844d55ef..b8c7e085a90d 100644
> --- a/drivers/mtd/spi-nor/otp.c
> +++ b/drivers/mtd/spi-nor/otp.c
> @@ -11,8 +11,8 @@
>   
>   #include "core.h"
>   
> -#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
> -#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
> +#define spi_nor_otp_region_len(nor) ((nor)->params[0]->otp.org->len)
> +#define spi_nor_otp_n_regions(nor) ((nor)->params[0]->otp.org->n_regions)

I think this should be also converted to static inline functions and use
spi_nor_get_params(nor, 0); instead of using arrays.
Can be done on the top of this series if it is the only one problem with it.

Thanks,
Michal

WARNING: multiple messages have this Message-ID (diff)
From: Michal Simek <michal.simek@amd.com>
To: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>,
	<broonie@kernel.org>, <miquel.raynal@bootlin.com>,
	<richard@nod.at>, <vigneshr@ti.com>, <jic23@kernel.org>,
	<tudor.ambarus@microchip.com>, <pratyush@kernel.org>,
	<sanju.mehta@amd.com>, <chin-ting_kuo@aspeedtech.com>,
	<clg@kaod.org>, <kdasu.kdev@gmail.com>, <f.fainelli@gmail.com>,
	<rjui@broadcom.com>, <sbranden@broadcom.com>,
	<eajames@linux.ibm.com>, <olteanv@gmail.com>, <han.xu@nxp.com>,
	<john.garry@huawei.com>, <shawnguo@kernel.org>,
	<s.hauer@pengutronix.de>, <narmstrong@baylibre.com>,
	<khilman@baylibre.com>, <matthias.bgg@gmail.com>,
	<haibo.chen@nxp.com>, <linus.walleij@linaro.org>,
	<daniel@zonque.org>, <haojian.zhuang@gmail.com>,
	<robert.jarzmik@free.fr>, <agross@kernel.org>,
	<bjorn.andersson@linaro.org>, <heiko@sntech.de>,
	<krzysztof.kozlowski@linaro.org>, <andi@etezian.org>,
	<mcoquelin.stm32@gmail.com>, <alexandre.torgue@foss.st.com>,
	<wens@csie.org>, <jernej.skrabec@gmail.com>,
	<samuel@sholland.org>, <masahisa.kojima@linaro.org>,
	<jaswinder.singh@linaro.org>, <rostedt@goodmis.org>,
	<mingo@redhat.com>, <l.stelmach@samsung.com>,
	<davem@davemloft.net>, <edumazet@google.com>, <kuba@kernel.org>,
	<pabeni@redhat.com>, <alex.aring@gmail.com>,
	<stefan@datenfreihafen.org>, <kvalo@kernel.org>,
	<thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
	<skomatineni@nvidia.com>, <sumit.semwal@linaro.org>,
	<christian.koenig@amd.com>, <j.neuschaefer@gmx.net>,
	<vireshk@kernel.org>, <rmfrfs@gmail.com>, <johan@kernel.org>,
	<elder@kernel.org>, <gregkh@linuxfoundation.org>
Cc: <git@amd.com>, <linux-spi@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <joel@jms.id.au>,
	<andrew@aj.id.au>, <radu_nicolae.pirea@upb.ro>,
	<nicolas.ferre@microchip.com>, <alexandre.belloni@bootlin.com>,
	<claudiu.beznea@microchip.com>,
	<bcm-kernel-feedback-list@broadcom.com>,
	<fancer.lancer@gmail.com>, <kernel@pengutronix.de>,
	<festevam@gmail.com>, <linux-imx@nxp.com>, <jbrunet@baylibre.com>,
	<martin.blumenstingl@googlemail.com>, <avifishman70@gmail.com>,
	<tmaimon77@gmail.com>, <tali.perry1@gmail.com>,
	<venture@google.com>, <yuenn@google.com>,
	<benjaminfair@google.com>, <yogeshgaur.83@gmail.com>,
	<konrad.dybcio@somainline.org>, <alim.akhtar@samsung.com>,
	<ldewangan@nvidia.com>, <linux-aspeed@lists.ozlabs.org>,
	<openbmc@lists.ozlabs.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-rpi-kernel@lists.infradead.org>,
	<linux-amlogic@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	<linux-arm-msm@vger.kernel.org>,
	<linux-rockchip@lists.infradead.org>,
	<linux-samsung-soc@vger.kernel.org>,
	<linux-stm32@st-md-mailman.stormreply.com>,
	<linux-sunxi@lists.linux.dev>, <linux-tegra@vger.kernel.org>,
	<netdev@vger.kernel.org>, <linux-wpan@vger.kernel.org>,
	<libertas-dev@lists.infradead.org>,
	<linux-wireless@vger.kernel.org>, <linux-mtd@lists.infradead.org>,
	<lars@metafoo.de>, <Michael.Hennerich@analog.com>,
	<linux-iio@vger.kernel.org>, <michael@walle.cc>,
	<palmer@dabbelt.com>, <linux-riscv@lists.infradead.org>,
	<linux-media@vger.kernel.org>, <dri-devel@lists.freedesktop.org>,
	<greybus-dev@lists.linaro.org>, <linux-staging@lists.linux.dev>,
	<amitrkcian2002@gmail.com>
Subject: Re: [PATCH v2 10/13] mtd: spi-nor: Add stacked memories support in spi-nor
Date: Mon, 23 Jan 2023 13:40:48 +0100	[thread overview]
Message-ID: <09534bb9-d9be-5433-5e7d-f9d40e30562e@amd.com> (raw)
In-Reply-To: <20230119185342.2093323-11-amit.kumar-mahapatra@amd.com>



On 1/19/23 19:53, Amit Kumar Mahapatra wrote:
> Each flash that is connected in stacked mode should have a separate
> parameter structure. So, the flash parameter member(*params) of the spi_nor
> structure is changed to an array (*params[2]). The array is used to store
> the parameters of each flash connected in stacked configuration.
> 
> The current implementation assumes that a maximum of two flashes are
> connected in stacked mode 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 is not aware of the chip_select values, for any incoming request
> SPI-NOR will decide the flash index with the help of individual flash size
> and the configuration type (single/stacked). SPI-NOR will pass on the flash
> index information to the SPI core & SPI driver by setting the appropriate
> bit in nor->spimem->spi->cs_index_mask. For example, if nth bit of
> nor->spimem->spi->cs_index_mask is set then the driver would
> assert/de-assert spi->chip_slect[n].
> 
> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> ---
>   drivers/mtd/spi-nor/core.c  | 282 +++++++++++++++++++++++++++++-------
>   drivers/mtd/spi-nor/core.h  |   4 +
>   drivers/mtd/spi-nor/otp.c   |   4 +-
>   include/linux/mtd/spi-nor.h |  12 +-
>   4 files changed, 246 insertions(+), 56 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 8a4a54bf2d0e..bb7326dc8b70 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1441,13 +1441,18 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
>   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;
> +	struct spi_nor_flash_parameter *params;
> +	u32 addr, len, offset, cur_cs_num = 0;
>   	uint32_t rem;
>   	int ret;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>   			(long long)instr->len);
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
>   	if (spi_nor_has_uniform_erase(nor)) {
>   		div_u64_rem(instr->len, mtd->erasesize, &rem);
>   		if (rem)
> @@ -1465,26 +1470,30 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
>   		unsigned long timeout;
>   
> -		ret = spi_nor_write_enable(nor);
> -		if (ret)
> -			goto erase_err;
> +		while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
> +			ret = spi_nor_write_enable(nor);
> +			if (ret)
> +				goto erase_err;
>   
> -		ret = spi_nor_erase_chip(nor);
> -		if (ret)
> -			goto erase_err;
> +			ret = spi_nor_erase_chip(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)(mtd->size / SZ_2M));
> -		ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
> -		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++;
> +		}
>   
>   	/* 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
> @@ -1493,12 +1502,26 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   	/* "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 < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   		while (len) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
>   			ret = spi_nor_write_enable(nor);
>   			if (ret)
>   				goto erase_err;
>   
> -			ret = spi_nor_erase_sector(nor, addr);
> +			offset = addr;
> +			if (nor->flags & SNOR_F_HAS_STACKED) {
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				offset -= (sz - params->size);
> +			}
> +
> +			ret = spi_nor_erase_sector(nor, offset);
>   			if (ret)
>   				goto erase_err;
>   
> @@ -1508,13 +1531,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   			addr += mtd->erasesize;
>   			len -= mtd->erasesize;
> +
> +			/*
> +			 * Flash cross over condition in stacked mode.
> +			 */
> +			if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) {
> +				cur_cs_num++;
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				sz += params->size;
> +			}
>   		}
>   
>   	/* erase multiple sectors */
>   	} else {
> -		ret = spi_nor_erase_multi_sectors(nor, addr, len);
> -		if (ret)
> -			goto erase_err;
> +		u64 erase_len = 0;
> +
> +		/* Determine the flash from which the operation need to start */
> +		while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			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);
> +			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;
> +		}
>   	}
>   
>   	ret = spi_nor_write_disable(nor);
> @@ -1713,7 +1768,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   			size_t *retlen, u_char *buf)
>   {
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
> -	ssize_t ret;
> +	struct spi_nor_flash_parameter *params;
> +	ssize_t ret, read_len;
> +	u32 cur_cs_num = 0;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>   
> @@ -1721,9 +1779,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   	if (ret)
>   		return ret;
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) {
> +		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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_read_data(nor, addr, len, buf);
> @@ -1735,11 +1807,22 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   		if (ret < 0)
>   			goto read_err;
>   
> -		WARN_ON(ret > len);
> +		WARN_ON(ret > read_len);
>   		*retlen += ret;
>   		buf += ret;
>   		from += ret;
>   		len -= ret;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 *
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && (from > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   	}
>   	ret = 0;
>   
> @@ -1759,13 +1842,22 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
>   	struct spi_nor_flash_parameter *params;
>   	size_t page_offset, page_remain, i;
> +	u32 page_size, cur_cs_num = 0;
>   	ssize_t ret;
> -	u32 page_size;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>   
>   	params = spi_nor_get_params(nor, 0);
>   	page_size = params->page_size;
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) {
> +		cur_cs_num++;
> +		params = spi_nor_get_params(nor, cur_cs_num);
> +		sz += params->size;
> +	}
>   
>   	ret = spi_nor_lock_and_prep(nor);
>   	if (ret)
> @@ -1790,6 +1882,10 @@ 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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_write_enable(nor);
> @@ -1806,6 +1902,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   			goto write_err;
>   		*retlen += written;
>   		i += written;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && ((to + i) > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
>   	}
>   
>   write_err:
> @@ -1918,8 +2023,6 @@ int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
>   static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   				   struct spi_mem_op *op)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -
>   	/*
>   	 * First test with 4 address bytes. The opcode itself might
>   	 * be a 3B addressing opcode but we don't care, because
> @@ -1928,7 +2031,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   	 */
>   	op->addr.nbytes = 4;
>   	if (!spi_mem_supports_op(nor->spimem, op)) {
> -		if (params->size > SZ_16M)
> +		if (nor->mtd.size > SZ_16M)
>   			return -EOPNOTSUPP;
>   
>   		/* If flash size <= 16MB, 3 address bytes are sufficient */
> @@ -2516,6 +2619,10 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
>   static void spi_nor_late_init_params(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct device_node *np = spi_nor_get_flash_node(nor);
> +	u64 flash_size[SNOR_FLASH_CNT_MAX];
> +	u32 idx = 0, i = 0;
> +	int rc;
>   
>   	if (nor->manufacturer && nor->manufacturer->fixups &&
>   	    nor->manufacturer->fixups->late_init)
> @@ -2533,6 +2640,36 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
>   	 */
>   	if (nor->flags & SNOR_F_HAS_LOCK && !params->locking_ops)
>   		spi_nor_init_default_locking_ops(nor);
> +	/*
> +	 * 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 = of_property_read_u64_index(np, "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->flags & SNOR_F_HAS_STACKED) {
> +		for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
> +			if (params) {
> +				memcpy(params, spi_nor_get_params(nor, 0), sizeof(*params));
> +				params->size = flash_size[idx];
> +				spi_nor_set_params(nor, idx, params);
> +			}
> +		}
> +	}
>   }
>   
>   /**
> @@ -2741,22 +2878,36 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
>    */
>   static int spi_nor_quad_enable(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
> -	if (!params->quad_enable)
> -		return 0;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params) {
> +			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;
> +			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 = 0x01 << idx;
>   
> -	return params->quad_enable(nor);
> +			err = params->quad_enable(nor);
> +			if (err)
> +				return err;
> +		}
> +	}
> +	return err;
>   }
>   
>   static int spi_nor_init(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -	int err;
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
>   	err = spi_nor_octal_dtr_enable(nor, true);
>   	if (err) {
> @@ -2797,9 +2948,19 @@ 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");
> -		err = params->set_4byte_addr_mode(nor, true);
> -		if (err && err != -ENOTSUPP)
> -			return err;
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				err = params->set_4byte_addr_mode(nor, true);
> +				if (err && err != -ENOTSUPP)
> +					return err;
> +			}
> +		}
>   	}
>   
>   	return 0;
> @@ -2915,19 +3076,31 @@ void spi_nor_restore(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params;
>   	int ret;
> +	int idx;
>   
>   	/* restore the addressing mode */
>   	if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
>   	    nor->flags & SNOR_F_BROKEN_RESET) {
> -		params = spi_nor_get_params(nor, 0);
> -		ret = params->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);
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				ret = params->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);
> +			}
> +		}
>   	}
>   
>   	if (nor->flags & SNOR_F_SOFT_RESET)
> @@ -2995,6 +3168,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
>   	struct mtd_info *mtd = &nor->mtd;
>   	struct device *dev = nor->dev;
> +	u64 total_sz = 0;
> +	int idx;
>   
>   	spi_nor_set_mtd_locking_ops(nor);
>   	spi_nor_set_mtd_otp_ops(nor);
> @@ -3010,7 +3185,12 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   		mtd->_erase = spi_nor_erase;
>   	mtd->writesize = params->writesize;
>   	mtd->writebufsize = params->page_size;
> -	mtd->size = params->size;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params)
> +			total_sz += params->size;
> +	}
> +	mtd->size = total_sz;
>   	mtd->_read = spi_nor_read;
>   	/* Might be already set by some SST flashes. */
>   	if (!mtd->_write)
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index f03b55cf7e6f..e94107cc465e 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -11,6 +11,9 @@
>   
>   #define SPI_NOR_MAX_ID_LEN	6
>   
> +/* In single configuration enable CS0 */
> +#define SPI_NOR_ENABLE_CS0     BIT(0)
> +
>   /* 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),			\
> @@ -130,6 +133,7 @@ enum spi_nor_option_flags {
>   	SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
>   	SNOR_F_SOFT_RESET	= BIT(12),
>   	SNOR_F_SWP_IS_VOLATILE	= BIT(13),
> +	SNOR_F_HAS_STACKED      = BIT(14),
>   };
>   
>   struct spi_nor_read_command {
> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
> index a9c0844d55ef..b8c7e085a90d 100644
> --- a/drivers/mtd/spi-nor/otp.c
> +++ b/drivers/mtd/spi-nor/otp.c
> @@ -11,8 +11,8 @@
>   
>   #include "core.h"
>   
> -#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
> -#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
> +#define spi_nor_otp_region_len(nor) ((nor)->params[0]->otp.org->len)
> +#define spi_nor_otp_n_regions(nor) ((nor)->params[0]->otp.org->n_regions)

I think this should be also converted to static inline functions and use
spi_nor_get_params(nor, 0); instead of using arrays.
Can be done on the top of this series if it is the only one problem with it.

Thanks,
Michal

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

WARNING: multiple messages have this Message-ID (diff)
From: Michal Simek <michal.simek@amd.com>
To: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>,
	<broonie@kernel.org>, <miquel.raynal@bootlin.com>,
	<richard@nod.at>, <vigneshr@ti.com>, <jic23@kernel.org>,
	<tudor.ambarus@microchip.com>, <pratyush@kernel.org>,
	<sanju.mehta@amd.com>, <chin-ting_kuo@aspeedtech.com>,
	<clg@kaod.org>, <kdasu.kdev@gmail.com>, <f.fainelli@gmail.com>,
	<rjui@broadcom.com>, <sbranden@broadcom.com>,
	<eajames@linux.ibm.com>, <olteanv@gmail.com>, <han.xu@nxp.com>,
	<john.garry@huawei.com>, <shawnguo@kernel.org>,
	<s.hauer@pengutronix.de>, <narmstrong@baylibre.com>,
	<khilman@baylibre.com>, <matthias.bgg@gmail.com>,
	<haibo.chen@nxp.com>, <linus.walleij@linaro.org>,
	<daniel@zonque.org>, <haojian.zhuang@gmail.com>,
	<robert.jarzmik@free.fr>, <agross@kernel.org>,
	<bjorn.andersson@linaro.org>, <heiko@sntech.de>,
	<krzysztof.kozlowski@linaro.org>, <andi@etezian.org>,
	<mcoquelin.stm32@gmail.com>, <alexandre.torgue@foss.st.com>,
	<wens@csie.org>, <jernej.skrabec@gmail.com>,
	<samuel@sholland.org>, <masahisa.kojima@linaro.org>,
	<jaswinder.singh@linaro.org>, <rostedt@goodmis.org>,
	<mingo@redhat.com>, <l.stelmach@samsung.com>,
	<davem@davemloft.net>, <edumazet@google.com>, <kuba@kernel.org>,
	<pabeni@redhat.com>, <alex.aring@gmail.com>,
	<stefan@datenfreihafen.org>, <kvalo@kernel.org>,
	<thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
	<skomatineni@nvidia.com>, <sumit.semwal@linaro.org>,
	<christian.koenig@amd.com>, <j.neuschaefer@gmx.net>,
	<vireshk@kernel.org>, <rmfrfs@gmail.com>, <johan@kernel.org>,
	<elder@kernel.org>, <gregkh@linuxfoundation.org>
Cc: <git@amd.com>, <linux-spi@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <joel@jms.id.au>,
	<andrew@aj.id.au>, <radu_nicolae.pirea@upb.ro>,
	<nicolas.ferre@microchip.com>, <alexandre.belloni@bootlin.com>,
	<claudiu.beznea@microchip.com>,
	<bcm-kernel-feedback-list@broadcom.com>,
	<fancer.lancer@gmail.com>, <kernel@pengutronix.de>,
	<festevam@gmail.com>, <linux-imx@nxp.com>, <jbrunet@baylibre.com>,
	<martin.blumenstingl@googlemail.com>, <avifishman70@gmail.com>,
	<tmaimon77@gmail.com>, <tali.perry1@gmail.com>,
	<venture@google.com>, <yuenn@google.com>,
	<benjaminfair@google.com>, <yogeshgaur.83@gmail.com>,
	<konrad.dybcio@somainline.org>, <alim.akhtar@samsung.com>,
	<ldewangan@nvidia.com>, <linux-aspeed@lists.ozlabs.org>,
	<openbmc@lists.ozlabs.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-rpi-kernel@lists.infradead.org>,
	<linux-amlogic@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	<linux-arm-msm@vger.kernel.org>,
	<linux-rockchip@lists.infradead.org>,
	<linux-samsung-soc@vger.kernel.org>,
	<linux-stm32@st-md-mailman.stormreply.com>,
	<linux-sunxi@lists.linux.dev>, <linux-tegra@vger.kernel.org>,
	<netdev@vger.kernel.org>, <linux-wpan@vger.kernel.org>,
	<libertas-dev@lists.infradead.org>,
	<linux-wireless@vger.kernel.org>, <linux-mtd@lists.infradead.org>,
	<lars@metafoo.de>, <Michael.Hennerich@analog.com>,
	<linux-iio@vger.kernel.org>, <michael@walle.cc>,
	<palmer@dabbelt.com>, <linux-riscv@lists.infradead.org>,
	<linux-media@vger.kernel.org>, <dri-devel@lists.freedesktop.org>,
	<greybus-dev@lists.linaro.org>, <linux-staging@lists.linux.dev>,
	<amitrkcian2002@gmail.com>
Subject: Re: [PATCH v2 10/13] mtd: spi-nor: Add stacked memories support in spi-nor
Date: Mon, 23 Jan 2023 13:40:48 +0100	[thread overview]
Message-ID: <09534bb9-d9be-5433-5e7d-f9d40e30562e@amd.com> (raw)
In-Reply-To: <20230119185342.2093323-11-amit.kumar-mahapatra@amd.com>



On 1/19/23 19:53, Amit Kumar Mahapatra wrote:
> Each flash that is connected in stacked mode should have a separate
> parameter structure. So, the flash parameter member(*params) of the spi_nor
> structure is changed to an array (*params[2]). The array is used to store
> the parameters of each flash connected in stacked configuration.
> 
> The current implementation assumes that a maximum of two flashes are
> connected in stacked mode 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 is not aware of the chip_select values, for any incoming request
> SPI-NOR will decide the flash index with the help of individual flash size
> and the configuration type (single/stacked). SPI-NOR will pass on the flash
> index information to the SPI core & SPI driver by setting the appropriate
> bit in nor->spimem->spi->cs_index_mask. For example, if nth bit of
> nor->spimem->spi->cs_index_mask is set then the driver would
> assert/de-assert spi->chip_slect[n].
> 
> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> ---
>   drivers/mtd/spi-nor/core.c  | 282 +++++++++++++++++++++++++++++-------
>   drivers/mtd/spi-nor/core.h  |   4 +
>   drivers/mtd/spi-nor/otp.c   |   4 +-
>   include/linux/mtd/spi-nor.h |  12 +-
>   4 files changed, 246 insertions(+), 56 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 8a4a54bf2d0e..bb7326dc8b70 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1441,13 +1441,18 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
>   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;
> +	struct spi_nor_flash_parameter *params;
> +	u32 addr, len, offset, cur_cs_num = 0;
>   	uint32_t rem;
>   	int ret;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
>   			(long long)instr->len);
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
>   	if (spi_nor_has_uniform_erase(nor)) {
>   		div_u64_rem(instr->len, mtd->erasesize, &rem);
>   		if (rem)
> @@ -1465,26 +1470,30 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
>   		unsigned long timeout;
>   
> -		ret = spi_nor_write_enable(nor);
> -		if (ret)
> -			goto erase_err;
> +		while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
> +			ret = spi_nor_write_enable(nor);
> +			if (ret)
> +				goto erase_err;
>   
> -		ret = spi_nor_erase_chip(nor);
> -		if (ret)
> -			goto erase_err;
> +			ret = spi_nor_erase_chip(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)(mtd->size / SZ_2M));
> -		ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
> -		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++;
> +		}
>   
>   	/* 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
> @@ -1493,12 +1502,26 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   	/* "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 < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   		while (len) {
> +			nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
>   			ret = spi_nor_write_enable(nor);
>   			if (ret)
>   				goto erase_err;
>   
> -			ret = spi_nor_erase_sector(nor, addr);
> +			offset = addr;
> +			if (nor->flags & SNOR_F_HAS_STACKED) {
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				offset -= (sz - params->size);
> +			}
> +
> +			ret = spi_nor_erase_sector(nor, offset);
>   			if (ret)
>   				goto erase_err;
>   
> @@ -1508,13 +1531,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>   
>   			addr += mtd->erasesize;
>   			len -= mtd->erasesize;
> +
> +			/*
> +			 * Flash cross over condition in stacked mode.
> +			 */
> +			if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) {
> +				cur_cs_num++;
> +				params = spi_nor_get_params(nor, cur_cs_num);
> +				sz += params->size;
> +			}
>   		}
>   
>   	/* erase multiple sectors */
>   	} else {
> -		ret = spi_nor_erase_multi_sectors(nor, addr, len);
> -		if (ret)
> -			goto erase_err;
> +		u64 erase_len = 0;
> +
> +		/* Determine the flash from which the operation need to start */
> +		while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
> +			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);
> +			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;
> +		}
>   	}
>   
>   	ret = spi_nor_write_disable(nor);
> @@ -1713,7 +1768,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   			size_t *retlen, u_char *buf)
>   {
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
> -	ssize_t ret;
> +	struct spi_nor_flash_parameter *params;
> +	ssize_t ret, read_len;
> +	u32 cur_cs_num = 0;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>   
> @@ -1721,9 +1779,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   	if (ret)
>   		return ret;
>   
> +	params = spi_nor_get_params(nor, 0);
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) {
> +		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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_read_data(nor, addr, len, buf);
> @@ -1735,11 +1807,22 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>   		if (ret < 0)
>   			goto read_err;
>   
> -		WARN_ON(ret > len);
> +		WARN_ON(ret > read_len);
>   		*retlen += ret;
>   		buf += ret;
>   		from += ret;
>   		len -= ret;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 *
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && (from > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
> +
>   	}
>   	ret = 0;
>   
> @@ -1759,13 +1842,22 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   	struct spi_nor *nor = mtd_to_spi_nor(mtd);
>   	struct spi_nor_flash_parameter *params;
>   	size_t page_offset, page_remain, i;
> +	u32 page_size, cur_cs_num = 0;
>   	ssize_t ret;
> -	u32 page_size;
> +	u64 sz;
>   
>   	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>   
>   	params = spi_nor_get_params(nor, 0);
>   	page_size = params->page_size;
> +	sz = params->size;
> +
> +	/* Determine the flash from which the operation need to start */
> +	while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) {
> +		cur_cs_num++;
> +		params = spi_nor_get_params(nor, cur_cs_num);
> +		sz += params->size;
> +	}
>   
>   	ret = spi_nor_lock_and_prep(nor);
>   	if (ret)
> @@ -1790,6 +1882,10 @@ 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);
> +
>   		addr = spi_nor_convert_addr(nor, addr);
>   
>   		ret = spi_nor_write_enable(nor);
> @@ -1806,6 +1902,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>   			goto write_err;
>   		*retlen += written;
>   		i += written;
> +
> +		/*
> +		 * Flash cross over condition in stacked mode.
> +		 */
> +		if ((nor->flags & SNOR_F_HAS_STACKED) && ((to + i) > sz - 1)) {
> +			cur_cs_num++;
> +			params = spi_nor_get_params(nor, cur_cs_num);
> +			sz += params->size;
> +		}
>   	}
>   
>   write_err:
> @@ -1918,8 +2023,6 @@ int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
>   static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   				   struct spi_mem_op *op)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -
>   	/*
>   	 * First test with 4 address bytes. The opcode itself might
>   	 * be a 3B addressing opcode but we don't care, because
> @@ -1928,7 +2031,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
>   	 */
>   	op->addr.nbytes = 4;
>   	if (!spi_mem_supports_op(nor->spimem, op)) {
> -		if (params->size > SZ_16M)
> +		if (nor->mtd.size > SZ_16M)
>   			return -EOPNOTSUPP;
>   
>   		/* If flash size <= 16MB, 3 address bytes are sufficient */
> @@ -2516,6 +2619,10 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
>   static void spi_nor_late_init_params(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct device_node *np = spi_nor_get_flash_node(nor);
> +	u64 flash_size[SNOR_FLASH_CNT_MAX];
> +	u32 idx = 0, i = 0;
> +	int rc;
>   
>   	if (nor->manufacturer && nor->manufacturer->fixups &&
>   	    nor->manufacturer->fixups->late_init)
> @@ -2533,6 +2640,36 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
>   	 */
>   	if (nor->flags & SNOR_F_HAS_LOCK && !params->locking_ops)
>   		spi_nor_init_default_locking_ops(nor);
> +	/*
> +	 * 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 = of_property_read_u64_index(np, "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->flags & SNOR_F_HAS_STACKED) {
> +		for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
> +			if (params) {
> +				memcpy(params, spi_nor_get_params(nor, 0), sizeof(*params));
> +				params->size = flash_size[idx];
> +				spi_nor_set_params(nor, idx, params);
> +			}
> +		}
> +	}
>   }
>   
>   /**
> @@ -2741,22 +2878,36 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
>    */
>   static int spi_nor_quad_enable(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
> -	if (!params->quad_enable)
> -		return 0;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params) {
> +			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;
> +			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 = 0x01 << idx;
>   
> -	return params->quad_enable(nor);
> +			err = params->quad_enable(nor);
> +			if (err)
> +				return err;
> +		}
> +	}
> +	return err;
>   }
>   
>   static int spi_nor_init(struct spi_nor *nor)
>   {
> -	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
> -	int err;
> +	struct spi_nor_flash_parameter *params;
> +	int err, idx;
>   
>   	err = spi_nor_octal_dtr_enable(nor, true);
>   	if (err) {
> @@ -2797,9 +2948,19 @@ 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");
> -		err = params->set_4byte_addr_mode(nor, true);
> -		if (err && err != -ENOTSUPP)
> -			return err;
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				err = params->set_4byte_addr_mode(nor, true);
> +				if (err && err != -ENOTSUPP)
> +					return err;
> +			}
> +		}
>   	}
>   
>   	return 0;
> @@ -2915,19 +3076,31 @@ void spi_nor_restore(struct spi_nor *nor)
>   {
>   	struct spi_nor_flash_parameter *params;
>   	int ret;
> +	int idx;
>   
>   	/* restore the addressing mode */
>   	if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
>   	    nor->flags & SNOR_F_BROKEN_RESET) {
> -		params = spi_nor_get_params(nor, 0);
> -		ret = params->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);
> +		for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +			params = spi_nor_get_params(nor, idx);
> +			if (params) {
> +				/*
> +				 * Select the appropriate CS index before
> +				 * issuing the command.
> +				 */
> +				nor->spimem->spi->cs_index_mask = 0x01 << idx;
> +				ret = params->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);
> +			}
> +		}
>   	}
>   
>   	if (nor->flags & SNOR_F_SOFT_RESET)
> @@ -2995,6 +3168,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   	struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
>   	struct mtd_info *mtd = &nor->mtd;
>   	struct device *dev = nor->dev;
> +	u64 total_sz = 0;
> +	int idx;
>   
>   	spi_nor_set_mtd_locking_ops(nor);
>   	spi_nor_set_mtd_otp_ops(nor);
> @@ -3010,7 +3185,12 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
>   		mtd->_erase = spi_nor_erase;
>   	mtd->writesize = params->writesize;
>   	mtd->writebufsize = params->page_size;
> -	mtd->size = params->size;
> +	for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
> +		params = spi_nor_get_params(nor, idx);
> +		if (params)
> +			total_sz += params->size;
> +	}
> +	mtd->size = total_sz;
>   	mtd->_read = spi_nor_read;
>   	/* Might be already set by some SST flashes. */
>   	if (!mtd->_write)
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index f03b55cf7e6f..e94107cc465e 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -11,6 +11,9 @@
>   
>   #define SPI_NOR_MAX_ID_LEN	6
>   
> +/* In single configuration enable CS0 */
> +#define SPI_NOR_ENABLE_CS0     BIT(0)
> +
>   /* 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),			\
> @@ -130,6 +133,7 @@ enum spi_nor_option_flags {
>   	SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
>   	SNOR_F_SOFT_RESET	= BIT(12),
>   	SNOR_F_SWP_IS_VOLATILE	= BIT(13),
> +	SNOR_F_HAS_STACKED      = BIT(14),
>   };
>   
>   struct spi_nor_read_command {
> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
> index a9c0844d55ef..b8c7e085a90d 100644
> --- a/drivers/mtd/spi-nor/otp.c
> +++ b/drivers/mtd/spi-nor/otp.c
> @@ -11,8 +11,8 @@
>   
>   #include "core.h"
>   
> -#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
> -#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
> +#define spi_nor_otp_region_len(nor) ((nor)->params[0]->otp.org->len)
> +#define spi_nor_otp_n_regions(nor) ((nor)->params[0]->otp.org->n_regions)

I think this should be also converted to static inline functions and use
spi_nor_get_params(nor, 0); instead of using arrays.
Can be done on the top of this series if it is the only one problem with it.

Thanks,
Michal

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

  reply	other threads:[~2023-01-23 12:41 UTC|newest]

Thread overview: 162+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-01-19 18:53 [PATCH v2 00/13] spi: Add support for stacked/parallel memories Amit Kumar Mahapatra
2023-01-19 18:53 ` Amit Kumar Mahapatra
2023-01-19 18:53 ` Amit Kumar Mahapatra
2023-01-19 18:53 ` Amit Kumar Mahapatra
2023-01-19 18:53 ` Amit Kumar Mahapatra
2023-01-19 18:53 ` [PATCH v2 01/13] spi: Add APIs in spi core to set/get spi->chip_select and spi->cs_gpiod Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-23 12:44   ` Michal Simek
2023-01-23 12:44     ` Michal Simek
2023-01-23 12:44     ` Michal Simek
2023-01-23 12:44     ` Michal Simek
2023-01-23 12:44     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 02/13] spi: Replace all spi->chip_select and spi->cs_gpiod references with function call Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-20  9:47   ` Heiko Stübner
2023-01-20  9:47     ` Heiko Stübner
2023-01-20  9:47     ` Heiko Stübner
2023-01-20  9:47     ` Heiko Stübner
2023-01-23 12:46   ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 13:10   ` Cédric Le Goater
2023-01-23 13:10     ` Cédric Le Goater
2023-01-23 13:10     ` Cédric Le Goater
2023-01-23 13:10     ` Cédric Le Goater
2023-01-23 13:10     ` Cédric Le Goater
2023-01-23 14:20   ` Dhruva Gole
2023-01-23 14:20     ` Dhruva Gole
2023-01-23 14:20     ` Dhruva Gole
2023-01-23 14:20     ` Dhruva Gole
2023-01-23 14:20     ` Dhruva Gole
2023-01-23 15:04   ` Serge Semin
2023-01-23 15:04     ` Serge Semin
2023-01-23 15:04     ` Serge Semin
2023-01-23 15:04     ` Serge Semin
2023-01-23 15:04     ` Serge Semin
2023-01-23 15:04     ` Serge Semin
2023-01-23 15:04     ` Serge Semin
2023-01-23 17:16   ` Patrice CHOTARD
2023-01-23 17:16     ` Patrice CHOTARD
2023-01-23 17:16     ` Patrice CHOTARD
2023-01-23 17:16     ` Patrice CHOTARD
2023-01-23 17:16     ` Patrice CHOTARD
2023-01-25  0:57   ` William Zhang
2023-01-25  0:57     ` William Zhang
2023-01-25  0:57     ` William Zhang
2023-01-25  0:57     ` William Zhang
2023-01-25  0:57     ` William Zhang
2023-02-01 15:15   ` Mark Brown
2023-02-01 15:15     ` Mark Brown
2023-02-01 15:15     ` Mark Brown
2023-02-01 15:15     ` Mark Brown
2023-02-01 15:15     ` Mark Brown
2023-01-19 18:53 ` [PATCH v2 03/13] net: " Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
     [not found]   ` <CGME20230120103014eucas1p24409332eeb60c62e22287f97339a2d91@eucas1p2.samsung.com>
2023-01-20 10:30     ` Lukasz Stelmach
2023-01-23 12:46   ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-23 12:46     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 04/13] iio: imu: " Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-21 16:52   ` Jonathan Cameron
2023-01-21 16:52     ` Jonathan Cameron
2023-01-21 16:52     ` Jonathan Cameron
2023-01-21 16:52     ` Jonathan Cameron
2023-01-21 16:52     ` Jonathan Cameron
2023-01-23 12:47   ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 05/13] mtd: devices: " Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-23 12:47   ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 06/13] staging: " Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-20 10:09   ` Greg KH
2023-01-20 10:09     ` Greg KH
2023-01-20 10:09     ` Greg KH
2023-01-20 10:09     ` Greg KH
2023-01-23 12:47   ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-23 12:47     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 07/13] platform/x86: serial-multi-instantiate: " Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-23 12:48   ` Michal Simek
2023-01-23 12:48     ` Michal Simek
2023-01-23 12:48     ` Michal Simek
2023-01-23 12:48     ` Michal Simek
2023-01-23 12:48     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 08/13] spi: Add stacked and parallel memories support in SPI core Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53 ` [PATCH v2 09/13] mtd: spi-nor: Add APIs to set/get nor->params Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53 ` [PATCH v2 10/13] mtd: spi-nor: Add stacked memories support in spi-nor Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-23 12:40   ` Michal Simek [this message]
2023-01-23 12:40     ` Michal Simek
2023-01-23 12:40     ` Michal Simek
2023-01-23 12:40     ` Michal Simek
2023-01-23 12:40     ` Michal Simek
2023-01-19 18:53 ` [PATCH v2 11/13] spi: spi-zynqmp-gqspi: Add stacked memories support in GQSPI driver Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53 ` [PATCH v2 12/13] mtd: spi-nor: Add parallel memories support in spi-nor Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53 ` [PATCH v2 13/13] spi: spi-zynqmp-gqspi: Add parallel memories support in GQSPI driver Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-19 18:53   ` Amit Kumar Mahapatra
2023-01-20  2:36   ` kernel test robot
2023-02-01 16:57 ` (subset) [PATCH v2 00/13] spi: Add support for stacked/parallel memories Mark Brown
2023-02-01 16:57   ` Mark Brown
2023-02-01 16:57   ` Mark Brown
2023-02-01 16:57   ` Mark Brown
2023-02-01 16:57   ` Mark Brown

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=09534bb9-d9be-5433-5e7d-f9d40e30562e@amd.com \
    --to=michal.simek@amd.com \
    --cc=Michael.Hennerich@analog.com \
    --cc=agross@kernel.org \
    --cc=alex.aring@gmail.com \
    --cc=alexandre.belloni@bootlin.com \
    --cc=alexandre.torgue@foss.st.com \
    --cc=alim.akhtar@samsung.com \
    --cc=amit.kumar-mahapatra@amd.com \
    --cc=amitrkcian2002@gmail.com \
    --cc=andi@etezian.org \
    --cc=andrew@aj.id.au \
    --cc=avifishman70@gmail.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=benjaminfair@google.com \
    --cc=bjorn.andersson@linaro.org \
    --cc=broonie@kernel.org \
    --cc=chin-ting_kuo@aspeedtech.com \
    --cc=christian.koenig@amd.com \
    --cc=claudiu.beznea@microchip.com \
    --cc=clg@kaod.org \
    --cc=daniel@zonque.org \
    --cc=davem@davemloft.net \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=eajames@linux.ibm.com \
    --cc=edumazet@google.com \
    --cc=elder@kernel.org \
    --cc=f.fainelli@gmail.com \
    --cc=fancer.lancer@gmail.com \
    --cc=festevam@gmail.com \
    --cc=git@amd.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=greybus-dev@lists.linaro.org \
    --cc=haibo.chen@nxp.com \
    --cc=han.xu@nxp.com \
    --cc=haojian.zhuang@gmail.com \
    --cc=heiko@sntech.de \
    --cc=j.neuschaefer@gmx.net \
    --cc=jaswinder.singh@linaro.org \
    --cc=jbrunet@baylibre.com \
    --cc=jernej.skrabec@gmail.com \
    --cc=jic23@kernel.org \
    --cc=joel@jms.id.au \
    --cc=johan@kernel.org \
    --cc=john.garry@huawei.com \
    --cc=jonathanh@nvidia.com \
    --cc=kdasu.kdev@gmail.com \
    --cc=kernel@pengutronix.de \
    --cc=khilman@baylibre.com \
    --cc=konrad.dybcio@somainline.org \
    --cc=krzysztof.kozlowski@linaro.org \
    --cc=kuba@kernel.org \
    --cc=kvalo@kernel.org \
    --cc=l.stelmach@samsung.com \
    --cc=lars@metafoo.de \
    --cc=ldewangan@nvidia.com \
    --cc=libertas-dev@lists.infradead.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-amlogic@lists.infradead.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-aspeed@lists.ozlabs.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-imx@nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=linux-rpi-kernel@lists.infradead.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=linux-staging@lists.linux.dev \
    --cc=linux-stm32@st-md-mailman.stormreply.com \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=linux-tegra@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=linux-wpan@vger.kernel.org \
    --cc=martin.blumenstingl@googlemail.com \
    --cc=masahisa.kojima@linaro.org \
    --cc=matthias.bgg@gmail.com \
    --cc=mcoquelin.stm32@gmail.com \
    --cc=michael@walle.cc \
    --cc=mingo@redhat.com \
    --cc=miquel.raynal@bootlin.com \
    --cc=narmstrong@baylibre.com \
    --cc=netdev@vger.kernel.org \
    --cc=nicolas.ferre@microchip.com \
    --cc=olteanv@gmail.com \
    --cc=openbmc@lists.ozlabs.org \
    --cc=pabeni@redhat.com \
    --cc=palmer@dabbelt.com \
    --cc=pratyush@kernel.org \
    --cc=radu_nicolae.pirea@upb.ro \
    --cc=richard@nod.at \
    --cc=rjui@broadcom.com \
    --cc=rmfrfs@gmail.com \
    --cc=robert.jarzmik@free.fr \
    --cc=rostedt@goodmis.org \
    --cc=s.hauer@pengutronix.de \
    --cc=samuel@sholland.org \
    --cc=sanju.mehta@amd.com \
    --cc=sbranden@broadcom.com \
    --cc=shawnguo@kernel.org \
    --cc=skomatineni@nvidia.com \
    --cc=stefan@datenfreihafen.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tali.perry1@gmail.com \
    --cc=thierry.reding@gmail.com \
    --cc=tmaimon77@gmail.com \
    --cc=tudor.ambarus@microchip.com \
    --cc=venture@google.com \
    --cc=vigneshr@ti.com \
    --cc=vireshk@kernel.org \
    --cc=wens@csie.org \
    --cc=yogeshgaur.83@gmail.com \
    --cc=yuenn@google.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.