Skip to content

Commit

Permalink
mtd: spi-nor: Add 4byte support for Zynq QSPI controller
Browse files Browse the repository at this point in the history
Zynq QSPI controller do not support 4byte addressing mode.
To access flashes greater that 16MB, added extended address
register read write in spi-nor framework to support 4byte
addressing for Zynq QSPI controller.

Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@xilinx.com>
Signed-off-by: Naga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
Signed-off-by: Ranjit Waghmode <ranjit.waghmode@xilinx.com>
Signed-off-by: Tejas Prajapati Rameshchandra <tejaspra@xilinx.com>
State: pending
[michals: Add changes from "mtd: spi-nor: Fix warnings"]
  • Loading branch information
Amit Kumar Mahapatra authored and Michal Simek committed Mar 22, 2021
1 parent 10a306e commit bd78733
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 1 deletion.
148 changes: 147 additions & 1 deletion drivers/mtd/spi-nor/core.c
Expand Up @@ -504,6 +504,49 @@ int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)
return ret;
}

/**
* read_ear - Get the extended/bank address register value
* @nor: Pointer to the flash control structure
*
* This routine reads the Extended/bank address register value
*
* Return: Negative if error occurred.
*/
static int read_ear(struct spi_nor *nor, struct flash_info *info)
{
int ret;
u8 code;

/* This is actually Spansion */
if (nor->jedec_id == CFI_MFR_AMD)
code = SPINOR_OP_BRRD;
/* This is actually Micron */
else if (nor->jedec_id == CFI_MFR_ST ||
nor->jedec_id == CFI_MFR_MACRONIX ||
nor->jedec_id == CFI_MFR_PMC)
code = SPINOR_OP_RDEAR;
else
return -EINVAL;
if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));

ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = nor->controller_ops->read_reg(nor, code, nor->bouncebuf, 1);
}
if (ret < 0) {
pr_err("error %d reading EAR\n", ret);
return ret;
}

return nor->bouncebuf[0];
}


/**
* spi_nor_xsr_ready() - Query the Status Register of the S3AN flash to see if
* the flash is ready for new commands.
Expand Down Expand Up @@ -993,6 +1036,48 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
return ret;
}

static int write_ear(struct spi_nor *nor, u32 addr)
{
u8 code;
u8 ear;
int ret;
struct mtd_info *mtd = &nor->mtd;

/* Wait until finished previous write command. */
if (spi_nor_wait_till_ready(nor))
return 1;

if (mtd->size <= (0x1000000) << nor->shift)
return 0;

addr = addr % (u32)mtd->size;
ear = addr >> 24;

if (!nor->isstacked && ear == nor->curbank)
return 0;

if (nor->isstacked && mtd->size <= 0x2000000)
return 0;

if (nor->jedec_id == CFI_MFR_AMD)
code = SPINOR_OP_BRWR;
if (nor->jedec_id == CFI_MFR_ST ||
nor->jedec_id == CFI_MFR_MACRONIX ||
nor->jedec_id == CFI_MFR_PMC) {
spi_nor_write_enable(nor);
code = SPINOR_OP_WREAR;
}
nor->bouncebuf[0] = ear;

ret = nor->controller_ops->write_reg(nor, code, nor->bouncebuf, 1);
if (ret < 0)
return ret;

nor->curbank = ear;

return 0;
}

/**
* spi_nor_erase_chip() - Erase the entire flash memory.
* @nor: pointer to 'struct spi_nor'.
Expand Down Expand Up @@ -1537,6 +1622,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
if (offset >= (mtd->size / 2))
offset = offset - (mtd->size / 2);
}
if (nor->addr_width == 3) {
/* Update Extended Address Register */
ret = write_ear(nor, offset);
if (ret)
goto erase_err;
}
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto erase_err;

Expand Down Expand Up @@ -2130,9 +2222,11 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
u32 stack_shift = 0;
u32 read_len = 0;
u32 rem_bank_len = 0;
u8 bank;
u8 is_ofst_odd = 0;
u_char *ptr;

#define OFFSET_16_MB 0x1000000
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);

if (nor->isparallel && (offset & 1)) {
Expand All @@ -2155,6 +2249,11 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
}

while (len) {
if (nor->addr_width == 3) {
bank = (u32)from / (OFFSET_16_MB << nor->shift);
rem_bank_len = ((OFFSET_16_MB << nor->shift) *
(bank + 1)) - from;
}
offset = from;
if (nor->isparallel == 1)
offset /= 2;
Expand All @@ -2163,6 +2262,13 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
if (offset >= (mtd->size / 2))
offset = offset - (mtd->size / 2);
}
if (nor->addr_width == 3) {
ret = write_ear(nor, offset);
if (ret) {
dev_err(nor->dev, "While writing ear register\n");
goto read_err;
}
}
if (len < rem_bank_len)
read_len = len;
else
Expand Down Expand Up @@ -2218,8 +2324,10 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t page_offset, page_remain, i;
ssize_t ret;
u32 offset, stack_shift = 0;
u8 bank = 0;
u32 rem_bank_len = 0;

#define OFFSET_16_MB 0x1000000
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
/*
* Cannot write to odd offset in parallel mode,
Expand Down Expand Up @@ -2247,6 +2355,11 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
ssize_t written;
loff_t addr = to + i;

if (nor->addr_width == 3) {
bank = (u32)to / (OFFSET_16_MB << nor->shift);
rem_bank_len = ((OFFSET_16_MB << nor->shift) *
(bank + 1)) - to;
}
/*
* If page_size is a power of two, the offset can be quickly
* calculated with an AND operation. On the other cases we
Expand All @@ -2272,6 +2385,16 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
offset = offset - (mtd->size / 2);
}

/* Die cross over issue is not handled */
if (nor->addr_width == 4)
rem_bank_len = (mtd->size >> stack_shift) - offset;
if (nor->addr_width == 3) {
ret = write_ear(nor, offset);
if (ret) {
dev_err(nor->dev, "While writing ear register\n");
goto write_err;
}
}
if (nor->isstacked == 1) {
if (len <= rem_bank_len) {
page_remain = min_t(size_t,
Expand Down Expand Up @@ -3165,6 +3288,8 @@ static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,

static int spi_nor_set_addr_width(struct spi_nor *nor)
{
struct device_node *np = spi_nor_get_flash_node(nor);
struct device_node *np_spi;
if (nor->addr_width) {
/* already configured from SFDP */
} else if (nor->info->addr_width) {
Expand All @@ -3174,8 +3299,26 @@ static int spi_nor_set_addr_width(struct spi_nor *nor)
}

if (nor->addr_width == 3 && nor->mtd.size > 0x1000000) {
#ifdef CONFIG_OF
np_spi = of_get_next_parent(np);
if (of_property_match_string(np_spi, "compatible",
"xlnx,zynq-qspi-1.0") >= 0) {
int status;

nor->addr_width = 3;
nor->params->set_4byte_addr_mode(nor, false);
status = read_ear(nor, (struct flash_info *)nor->info);
if (status < 0)
dev_warn(nor->dev, "failed to read ear reg\n");
else
nor->curbank = status & EAR_SEGMENT_MASK;
} else {
#endif
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
nor->addr_width = 4;
#ifdef CONFIG_OF
}
#endif
}

if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
Expand Down Expand Up @@ -3599,6 +3742,9 @@ static void spi_nor_shutdown(struct spi_mem *spimem)
{
struct spi_nor *nor = spi_mem_get_drvdata(spimem);

if (nor->addr_width == 3 &&
(nor->mtd.size >> nor->shift) > 0x1000000)
write_ear(nor, 0);
spi_nor_restore(nor);
}

Expand Down
3 changes: 3 additions & 0 deletions include/linux/mtd/spi-nor.h
Expand Up @@ -141,6 +141,9 @@
#define FSR_P_ERR BIT(4) /* Program operation status */
#define FSR_PT_ERR BIT(1) /* Protection error bit */

/* Extended/Bank Address Register bits */
#define EAR_SEGMENT_MASK 0x7 /* 128 Mb segment mask */

enum read_mode {
SPI_NOR_NORMAL = 0,
SPI_NOR_FAST,
Expand Down

0 comments on commit bd78733

Please sign in to comment.