Skip to content

Commit c87c9b1

Browse files
Kuwano-sanambarus
authored andcommitted
mtd: spi-nor: spansion: Determine current address mode
Internal address mode (3- or 4-byte) affects to the address length in Read Any Reg op. Read Any Reg op is used in SMPT parse and other setup functions. Current driver assumes that address mode is factory default but users can change it via volatile and non-volatile registers. Current address mode can be checked by CFR2V[7] but Read Any Reg op is needed to read CFR2V (chicken-and-egg). Introduce a way to determine current address mode by comparing status register 1 values read by different address length. Suggested-by: Tudor Ambarus <tudor.ambarus@linaro.org> Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com> Link: https://lore.kernel.org/r/20230331074606.3559258-11-tudor.ambarus@linaro.org Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
1 parent b6094ac commit c87c9b1

1 file changed

Lines changed: 128 additions & 3 deletions

File tree

drivers/mtd/spi-nor/spansion.c

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
1515
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
1616
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
17+
#define SPINOR_REG_CYPRESS_STR1V 0x00800000
1718
#define SPINOR_REG_CYPRESS_CFR1V 0x00800002
1819
#define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */
1920
#define SPINOR_REG_CYPRESS_CFR2V 0x00800003
2021
#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb
22+
#define SPINOR_REG_CYPRESS_CFR2_ADRBYT BIT(7)
2123
#define SPINOR_REG_CYPRESS_CFR3V 0x00800004
2224
#define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */
2325
#define SPINOR_REG_CYPRESS_CFR5V 0x00800006
@@ -188,6 +190,117 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
188190
return 0;
189191
}
190192

193+
/**
194+
* cypress_nor_determine_addr_mode_by_sr1() - Determine current address mode
195+
* (3 or 4-byte) by querying status
196+
* register 1 (SR1).
197+
* @nor: pointer to a 'struct spi_nor'
198+
* @addr_mode: ponter to a buffer where we return the determined
199+
* address mode.
200+
*
201+
* This function tries to determine current address mode by comparing SR1 value
202+
* from RDSR1(no address), RDAR(3-byte address), and RDAR(4-byte address).
203+
*
204+
* Return: 0 on success, -errno otherwise.
205+
*/
206+
static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor,
207+
u8 *addr_mode)
208+
{
209+
struct spi_mem_op op =
210+
CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_STR1V, 0,
211+
nor->bouncebuf);
212+
bool is3byte, is4byte;
213+
int ret;
214+
215+
ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]);
216+
if (ret)
217+
return ret;
218+
219+
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
220+
if (ret)
221+
return ret;
222+
223+
is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]);
224+
225+
op = (struct spi_mem_op)
226+
CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0,
227+
nor->bouncebuf);
228+
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
229+
if (ret)
230+
return ret;
231+
232+
is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]);
233+
234+
if (is3byte == is4byte)
235+
return -EIO;
236+
if (is3byte)
237+
*addr_mode = 3;
238+
else
239+
*addr_mode = 4;
240+
241+
return 0;
242+
}
243+
244+
/**
245+
* cypress_nor_set_addr_mode_nbytes() - Set the number of address bytes mode of
246+
* current address mode.
247+
* @nor: pointer to a 'struct spi_nor'
248+
*
249+
* Determine current address mode by reading SR1 with different methods, then
250+
* query CFR2V[7] to confirm. If determination is failed, force enter to 4-byte
251+
* address mode.
252+
*
253+
* Return: 0 on success, -errno otherwise.
254+
*/
255+
static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor)
256+
{
257+
struct spi_mem_op op;
258+
u8 addr_mode;
259+
int ret;
260+
261+
/*
262+
* Read SR1 by RDSR1 and RDAR(3- AND 4-byte addr). Use write enable
263+
* that sets bit-1 in SR1.
264+
*/
265+
ret = spi_nor_write_enable(nor);
266+
if (ret)
267+
return ret;
268+
ret = cypress_nor_determine_addr_mode_by_sr1(nor, &addr_mode);
269+
if (ret) {
270+
ret = spi_nor_set_4byte_addr_mode(nor, true);
271+
if (ret)
272+
return ret;
273+
return spi_nor_write_disable(nor);
274+
}
275+
ret = spi_nor_write_disable(nor);
276+
if (ret)
277+
return ret;
278+
279+
/*
280+
* Query CFR2V and make sure no contradiction between determined address
281+
* mode and CFR2V[7].
282+
*/
283+
op = (struct spi_mem_op)
284+
CYPRESS_NOR_RD_ANY_REG_OP(addr_mode, SPINOR_REG_CYPRESS_CFR2V,
285+
0, nor->bouncebuf);
286+
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
287+
if (ret)
288+
return ret;
289+
290+
if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR2_ADRBYT) {
291+
if (addr_mode != 4)
292+
return spi_nor_set_4byte_addr_mode(nor, true);
293+
} else {
294+
if (addr_mode != 3)
295+
return spi_nor_set_4byte_addr_mode(nor, true);
296+
}
297+
298+
nor->params->addr_nbytes = addr_mode;
299+
nor->params->addr_mode_nbytes = addr_mode;
300+
301+
return 0;
302+
}
303+
191304
/**
192305
* cypress_nor_set_page_size() - Set page size which corresponds to the flash
193306
* configuration.
@@ -227,9 +340,9 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor,
227340
struct spi_mem_op op;
228341
int ret;
229342

230-
/* 4-byte address mode is enabled by default */
231-
nor->params->addr_nbytes = 4;
232-
nor->params->addr_mode_nbytes = 4;
343+
ret = cypress_nor_set_addr_mode_nbytes(nor);
344+
if (ret)
345+
return ret;
233346

234347
/* Read Architecture Configuration Register (ARCFN) */
235348
op = (struct spi_mem_op)
@@ -280,6 +393,12 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
280393
const struct sfdp_parameter_header *bfpt_header,
281394
const struct sfdp_bfpt *bfpt)
282395
{
396+
int ret;
397+
398+
ret = cypress_nor_set_addr_mode_nbytes(nor);
399+
if (ret)
400+
return ret;
401+
283402
/* Replace Quad Enable with volatile version */
284403
nor->params->quad_enable = cypress_nor_quad_enable_volatile;
285404

@@ -375,6 +494,12 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
375494
const struct sfdp_parameter_header *bfpt_header,
376495
const struct sfdp_bfpt *bfpt)
377496
{
497+
int ret;
498+
499+
ret = cypress_nor_set_addr_mode_nbytes(nor);
500+
if (ret)
501+
return ret;
502+
378503
return cypress_nor_set_page_size(nor);
379504
}
380505

0 commit comments

Comments
 (0)