Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…oot-spi

- Support Infineon S28HS02GT (Takahiro)
  • Loading branch information
trini committed Jan 29, 2024
2 parents 41a737b + 16dd109 commit 8986be5
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 154 deletions.
224 changes: 85 additions & 139 deletions drivers/mtd/spi/spi-nor-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,18 +331,42 @@ static int spansion_read_any_reg(struct spi_nor *nor, u32 addr, u8 dummy,
u8 *val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDAR, 1),
SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 1),
SPI_MEM_OP_DUMMY(dummy / 8, 1),
SPI_MEM_OP_DATA_IN(1, NULL, 1));
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0),
SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 0),
SPI_MEM_OP_DUMMY(dummy, 0),
SPI_MEM_OP_DATA_IN(1, NULL, 0));
u8 buf[2];
int ret;

spi_nor_setup_op(nor, &op, nor->reg_proto);

/*
* In Octal DTR mode, the number of address bytes is always 4 regardless
* of addressing mode setting.
*/
if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR)
op.addr.nbytes = 4;

/*
* We don't want to read only one byte in DTR mode. So, read 2 and then
* discard the second byte.
*/
if (spi_nor_protocol_is_dtr(nor->reg_proto))
op.data.nbytes = 2;

return spi_nor_read_write_reg(nor, &op, val);
ret = spi_nor_read_write_reg(nor, &op, buf);
if (ret)
return ret;

*val = buf[0];

return 0;
}

static int spansion_write_any_reg(struct spi_nor *nor, u32 addr, u8 val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRAR, 1),
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, NULL, 1));
Expand Down Expand Up @@ -714,7 +738,7 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
*/
static int spansion_sr_ready(struct spi_nor *nor, u32 addr_base, u8 dummy)
{
u32 reg_addr = addr_base + SPINOR_REG_ADDR_STR1V;
u32 reg_addr = addr_base + SPINOR_REG_CYPRESS_STR1V;
u8 sr;
int ret;

Expand All @@ -728,7 +752,7 @@ static int spansion_sr_ready(struct spi_nor *nor, u32 addr_base, u8 dummy)
else
dev_dbg(nor->dev, "Programming Error occurred\n");

nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
nor->write_reg(nor, SPINOR_OP_CYPRESS_CLPEF, NULL, 0);
return -EIO;
}

Expand Down Expand Up @@ -1856,7 +1880,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base,
u8 dummy)
{
u32 addr = addr_base + SPINOR_REG_ADDR_CFR1V;
u32 addr = addr_base + SPINOR_REG_CYPRESS_CFR1V;

u8 cr;
int ret;
Expand Down Expand Up @@ -3293,11 +3317,11 @@ static int s25fs_s_setup(struct spi_nor *nor, const struct flash_info *info,
* Read CR3V to check if uniform sector is selected. If not, assign an
* erase hook that supports non-uniform erase.
*/
ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V,
ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V,
S25FS_S_RDAR_DUMMY, &cfr3v);
if (ret)
return ret;
if (!(cfr3v & CFR3V_UNHYSA))
if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_UNISECT))
nor->erase = s25fs_s_erase_non_uniform;

return spi_nor_default_setup(nor, info, params);
Expand Down Expand Up @@ -3346,13 +3370,13 @@ static struct spi_nor_fixups s25fs_s_fixups = {
.post_sfdp = s25fs_s_post_sfdp_fixup,
};

static int s25_mdp_ready(struct spi_nor *nor)
static int s25_s28_mdp_ready(struct spi_nor *nor)
{
u32 addr;
int ret;

for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
ret = spansion_sr_ready(nor, addr, 0);
ret = spansion_sr_ready(nor, addr, nor->rdsr_dummy);
if (!ret)
return ret;
}
Expand All @@ -3374,15 +3398,15 @@ static int s25_quad_enable(struct spi_nor *nor)
return 0;
}

static int s25_erase_non_uniform(struct spi_nor *nor, loff_t addr)
static int s25_s28_erase_non_uniform(struct spi_nor *nor, loff_t addr)
{
/* Support 32 x 4KB sectors at bottom */
return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0,
SZ_128K);
}

static int s25_setup(struct spi_nor *nor, const struct flash_info *info,
const struct spi_nor_flash_parameter *params)
static int s25_s28_setup(struct spi_nor *nor, const struct flash_info *info,
const struct spi_nor_flash_parameter *params)
{
int ret;
u8 cr;
Expand All @@ -3396,7 +3420,8 @@ static int s25_setup(struct spi_nor *nor, const struct flash_info *info,
* uniform 128KB only due to complexity of non-uniform layout.
*/
if (nor->info->id[4] == S25FS256T_ID4) {
ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_ARCFN, 8, &cr);
ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_ARCFN, 8,
&cr);
if (ret)
return ret;

Expand All @@ -3410,31 +3435,31 @@ static int s25_setup(struct spi_nor *nor, const struct flash_info *info,
* Read CFR3V to check if uniform sector is selected. If not, assign an
* erase hook that supports non-uniform erase.
*/
ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V, 0, &cr);
ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V, 0, &cr);
if (ret)
return ret;
if (!(cr & CFR3V_UNHYSA))
nor->erase = s25_erase_non_uniform;
if (!(cr & SPINOR_REG_CYPRESS_CFR3_UNISECT))
nor->erase = s25_s28_erase_non_uniform;

/*
* For the multi-die package parts, the ready() hook is needed to check
* all dies' status via read any register.
*/
if (nor->mtd.size > SZ_128M)
nor->ready = s25_mdp_ready;
nor->ready = s25_s28_mdp_ready;

return spi_nor_default_setup(nor, info, params);
}

static void s25_default_init(struct spi_nor *nor)
{
nor->setup = s25_setup;
nor->setup = s25_s28_setup;
}

static int s25_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *header,
const struct sfdp_bfpt *bfpt,
struct spi_nor_flash_parameter *params)
static int s25_s28_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *header,
const struct sfdp_bfpt *bfpt,
struct spi_nor_flash_parameter *params)
{
int ret;
u32 addr;
Expand Down Expand Up @@ -3474,12 +3499,13 @@ static int s25_post_bfpt_fixup(struct spi_nor *nor,
* dies are configured to 512B buffer.
*/
for (addr = 0; addr < params->size; addr += SZ_128M) {
ret = spansion_read_any_reg(nor, addr + SPINOR_REG_ADDR_CFR3V,
0, &cfr3v);
ret = spansion_read_any_reg(nor,
addr + SPINOR_REG_CYPRESS_CFR3V, 0,
&cfr3v);
if (ret)
return ret;

if (!(cfr3v & CFR3V_PGMBUF)) {
if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_PGSZ)) {
params->page_size = 256;
return 0;
}
Expand Down Expand Up @@ -3507,7 +3533,7 @@ static void s25_post_sfdp_fixup(struct spi_nor *nor,

static struct spi_nor_fixups s25_fixups = {
.default_init = s25_default_init,
.post_bfpt = s25_post_bfpt_fixup,
.post_bfpt = s25_s28_post_bfpt_fixup,
.post_sfdp = s25_post_sfdp_fixup,
};

Expand Down Expand Up @@ -3539,97 +3565,57 @@ static struct spi_nor_fixups s25fl256l_fixups = {
*/
static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor)
{
struct spi_mem_op op;
u32 addr;
u8 buf;
u8 addr_width = 3;
int ret;

/* Use 24 dummy cycles for memory array reads. */
ret = write_enable(nor);
if (ret)
return ret;

buf = SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR2V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, &buf, 1));
ret = spi_mem_exec_op(nor->spi, &op);
if (ret) {
dev_warn(nor->dev,
"failed to set default memory latency value: %d\n",
ret);
return ret;
}
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
/* Use 24 dummy cycles for memory array reads. */
for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
ret = spansion_read_any_reg(nor,
addr + SPINOR_REG_CYPRESS_CFR2V, 0,
&buf);
if (ret)
return ret;

buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK;
buf |= SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24;
ret = spansion_write_any_reg(nor,
addr + SPINOR_REG_CYPRESS_CFR2V,
buf);
if (ret) {
dev_warn(nor->dev, "failed to set default memory latency value: %d\n", ret);
return ret;
}
}
nor->read_dummy = 24;

/* Set the octal and DTR enable bits. */
ret = write_enable(nor);
if (ret)
return ret;

/* Set the octal and DTR enable bits. */
buf = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR5V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, &buf, 1));
ret = spi_mem_exec_op(nor->spi, &op);
if (ret) {
dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
return ret;
for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
ret = spansion_write_any_reg(nor,
addr + SPINOR_REG_CYPRESS_CFR5V,
buf);
if (ret) {
dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
return ret;
}
}

return 0;
}

static int s28hx_t_erase_non_uniform(struct spi_nor *nor, loff_t addr)
{
/* Factory default configuration: 32 x 4 KiB sectors at bottom. */
return spansion_erase_non_uniform(nor, addr, SPINOR_OP_S28_SE_4K,
0, SZ_128K);
}

static int s28hx_t_setup(struct spi_nor *nor, const struct flash_info *info,
const struct spi_nor_flash_parameter *params)
{
struct spi_mem_op op;
u8 buf;
u8 addr_width = 3;
int ret;

ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;

/*
* Check CFR3V to check if non-uniform sector mode is selected. If it
* is, set the erase hook to the non-uniform erase procedure.
*/
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
SPI_MEM_OP_ADDR(addr_width,
SPINOR_REG_CYPRESS_CFR3V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, &buf, 1));

ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;

if (!(buf & SPINOR_REG_CYPRESS_CFR3_UNISECT))
nor->erase = s28hx_t_erase_non_uniform;

return spi_nor_default_setup(nor, info, params);
}

static void s28hx_t_default_init(struct spi_nor *nor)
{
nor->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
nor->setup = s28hx_t_setup;
nor->setup = s25_s28_setup;
}

static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor,
Expand Down Expand Up @@ -3663,50 +3649,10 @@ static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor,
params->rdsr_addr_nbytes = 4;
}

static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt,
struct spi_nor_flash_parameter *params)
{
struct spi_mem_op op;
u8 buf;
u8 addr_width = 3;
int ret;

/*
* The BFPT table advertises a 512B page size but the page size is
* actually configurable (with the default being 256B). Read from
* CFR3V[4] and set the correct size.
*/
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR3V, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, &buf, 1));
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;

if (buf & SPINOR_REG_CYPRESS_CFR3_PGSZ)
params->page_size = 512;
else
params->page_size = 256;

/*
* The BFPT advertises that it supports 4k erases, and the datasheet
* says the same. But 4k erases did not work when testing. So, use 256k
* erases for now.
*/
nor->erase_opcode = SPINOR_OP_SE_4B;
nor->mtd.erasesize = 0x40000;

return 0;
}

static struct spi_nor_fixups s28hx_t_fixups = {
.default_init = s28hx_t_default_init,
.post_sfdp = s28hx_t_post_sfdp_fixup,
.post_bfpt = s28hx_t_post_bfpt_fixup,
.post_bfpt = s25_s28_post_bfpt_fixup,
};
#endif /* CONFIG_SPI_FLASH_S28HX_T */

Expand Down
Loading

0 comments on commit 8986be5

Please sign in to comment.