Skip to content
Permalink
Browse files
mtd: spi-nor: sst: Add Individual Block Locking support
Parse optional Manufacturer Specific SFDP and discover the memory
organization for the Individual Block Locking support. Add support
for invidivual Block Locking. Tested with sst26vf064b.

Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
  • Loading branch information
ambarus committed Apr 15, 2021
1 parent 50f8849 commit 4953dd5ca5cc7613b46aec1b26713518994f7aa1
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 8 deletions.
@@ -157,17 +157,17 @@ static int spi_nor_spimem_exec_op(struct spi_nor *nor, struct spi_mem_op *op)
return spi_mem_exec_op(nor->spimem, op);
}

static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
u8 *buf, size_t len)
int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
u8 *buf, size_t len)
{
if (spi_nor_protocol_is_dtr(nor->reg_proto))
return -EOPNOTSUPP;

return nor->controller_ops->read_reg(nor, opcode, buf, len);
}

static int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
const u8 *buf, size_t len)
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
const u8 *buf, size_t len)
{
if (spi_nor_protocol_is_dtr(nor->reg_proto))
return -EOPNOTSUPP;
@@ -518,6 +518,10 @@ extern const struct spi_nor_manufacturer spi_nor_winbond;
extern const struct spi_nor_manufacturer spi_nor_xilinx;
extern const struct spi_nor_manufacturer spi_nor_xmc;

int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
u8 *buf, size_t len);
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
const u8 *buf, size_t len);
void spi_nor_spimem_setup_op(const struct spi_nor *nor,
struct spi_mem_op *op,
const enum spi_nor_protocol proto);
@@ -12,10 +12,6 @@
#include "core.h"

#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb)
#define SFDP_PARAM_HEADER_PTP(p) \
(((p)->parameter_table_pointer[2] << 16) | \
((p)->parameter_table_pointer[1] << 8) | \
((p)->parameter_table_pointer[0] << 0))
#define SFDP_PARAM_HEADER_PARAM_LEN(p) ((p)->length * 4)

#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */
@@ -7,6 +7,11 @@
#ifndef __LINUX_MTD_SFDP_H
#define __LINUX_MTD_SFDP_H

#define SFDP_PARAM_HEADER_PTP(p) \
(((p)->parameter_table_pointer[2] << 16) | \
((p)->parameter_table_pointer[1] << 8) | \
((p)->parameter_table_pointer[0] << 0))

/* SFDP revisions */
#define SFDP_JESD216_MAJOR 1
#define SFDP_JESD216_MINOR 0
@@ -4,12 +4,104 @@
* Copyright (C) 2014, Freescale Semiconductor, Inc.
*/

#include <linux/bitfield.h>
#include <linux/mtd/spi-nor.h>

#include "core.h"

#define SPINOR_OP_SST26_RBPR 0x72 /* Read Block-Protection Register */
#define SPINOR_OP_SST26_WBPR 0x42 /* Write Block-Protection Register */


#define SST26VF_CR_BPNV BIT(3)

/*
* SST26 Memory Organization:
* 4 * 8 KByte blocks (read and write protection bits)
* 1 * 32 KByte block (write protection bits)
* (mtd->size / SZ_64K - 2) * 64Kbyte blocks (write protection bits)
* 1 * 32 KByte block (write protection bits)
* 4 * 8 KByte blocks (read and write protection bits)
*/
#define spi_nor_sst26_bpr_len(n_sectors) \
((((n_sectors) - 2 + 2 + 2 * (4 + 4)) + BITS_PER_BYTE - 1) / BITS_PER_BYTE)

/**
* spi_nor_sst_rbpr() - Read Block-Protection Register on SPI NOR SST26 family.
* @nor: pointer to 'struct spi_nor'.
* @bpr: pointer to DMA-able buffer where the value of the
* Block-Protection Register will be written.
* @len: number of bytes to write to the Block-Protection Register.
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_sst26_rbpr(struct spi_nor *nor, u8 *bpr, size_t len)
{
int ret;

if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SST26_RBPR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(len, bpr, 0));

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

ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_SST26_RBPR,
bpr, len);
}

if (ret)
dev_dbg(nor->dev, "error %d reading SST26 BPR\n", ret);

return ret;
}

/**
* spi_nor_sst26_wbpr() - Write Block-Protection Register on
* SPI NOR SST26 family.
* @nor: pointer to 'struct spi_nor'.
* @bpr: pointer to DMA-able buffer to write to the Block-Protection
* Register.
* @len: number of bytes to write to the Block-Protection Register.
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_sst26_wbpr(struct spi_nor *nor, const u8 *bpr, size_t len)
{
int ret;

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

if (nor->spimem) {
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SST26_WBPR, 0),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, bpr, 0));

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

ret = spi_mem_exec_op(nor->spimem, &op);
} else {
ret = spi_nor_controller_ops_write_reg(nor,
SPINOR_OP_SST26_WBPR,
bpr, len);
}

if (ret) {
dev_dbg(nor->dev, "error %d writing SST26 BPR\n", ret);
return ret;
}

return spi_nor_wait_till_ready(nor);
}

static int sst26vf_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
return -EOPNOTSUPP;
@@ -187,9 +279,117 @@ static const struct spi_nor_fixups sst_fixups = {
.post_sfdp = sst_post_sfdp_fixups,
};

/*
* The manufacturer specific manufacturer table is uniquely identified by the
* SFDP_SST_ID. The MSB value (0x01) indicates the bank number of JEDEC JEP106
* and the LSB value (0xbf) indicates the Manufacturer ID.
*/
#define SFDP_SST_ID 0x01bf

/* SST Manufacturer Parameter Table definitions. */
#define SST_MPT_IBP_MAP_DWORD_OFFSET 0x13
#define SST_MPT_IBP_MAP_SECTIONS_COUNT 5
#define SST_MPT_IBP_MAP_SECTOR_TYPE GENMASK(7, 0)
#define SST_MPT_IBP_MAP_N_SECTORS GENMASK(15, 8)
#define SST_MPT_IBP_MAP_BIT_START GENMASK(23, 16)
#define SST_MPT_IBP_MAP_BIT_END GENMASK(31, 24)

struct sst_mpt_ibp_section {
u8 sector_type;
u8 n_sectors;
s8 bpr_bit_start;
s8 bpr_bit_end;
u32 block_size;
};

struct sst_priv {
struct sst_mpt_ibp_section section[SST_MPT_IBP_MAP_SECTIONS_COUNT];
};

static int sst_mpt_bpr_map_init(struct spi_nor *nor, const u32 *sst_mpt)
{
struct sst_priv *sst_priv = nor->params->priv;
struct sst_mpt_ibp_section *section = sst_priv->section;
struct spi_nor_erase_type *erase_type =
nor->params->erase_map.erase_type;
unsigned int i, j, k;

j = SST_MPT_IBP_MAP_DWORD_OFFSET;
for (i = 0; i < SST_MPT_IBP_MAP_SECTIONS_COUNT; i++) {
section[i].sector_type = FIELD_GET(SST_MPT_IBP_MAP_SECTOR_TYPE,
sst_mpt[j]);
section[i].n_sectors = FIELD_GET(SST_MPT_IBP_MAP_N_SECTORS,
sst_mpt[j]);
section[i].bpr_bit_start = FIELD_GET(SST_MPT_IBP_MAP_BIT_START,
sst_mpt[j]);
section[i].bpr_bit_end = FIELD_GET(SST_MPT_IBP_MAP_BIT_END,
sst_mpt[j]);
for (k = 0; k < SNOR_ERASE_TYPE_MAX; k++)
if (section[i].sector_type == (erase_type[k].idx + 1))
section[i].block_size = erase_type[k].size;

j++;
dev_err(nor->dev,"section %d sector_type = %02x, n_sectors = "
"%02x bpr_bit_start = %02x bpr_bit_end = %02x, "
"block_size = %u\n",
i, section[i].sector_type, section[i].n_sectors,
section[i].bpr_bit_start, section[i].bpr_bit_end,
section[i].block_size);
}

return 0;
}

static int sst_manufacturer_specific_sfdp_parse(struct spi_nor *nor,
const struct sfdp_parameter_header *param_header)
{
struct sst_priv *sst_priv;
u32 *sst_mpt;
size_t size;
u32 addr;
int ret;
unsigned int i;

size = param_header->length * sizeof(u32);
sst_mpt = kmalloc(size, GFP_KERNEL);
if (!sst_mpt)
return -ENOMEM;

addr = SFDP_PARAM_HEADER_PTP(param_header);
ret = spi_nor_read_sfdp(nor, addr, size, sst_mpt);
if (ret)
goto out;
/* Fix endianness of the BFPT DWORDs. */
le32_to_cpu_array(sst_mpt, param_header->length);

for (i = 0; i < param_header->length; i++)
dev_err(nor->dev, "sst_mpt[%d] = %08x\n", i, sst_mpt[i]);

sst_priv = devm_kzalloc(nor->dev, sizeof(*sst_priv), GFP_KERNEL);
if (!sst_priv) {
ret = -ENOMEM;
goto out;
}
nor->params->priv = sst_priv;

ret = sst_mpt_bpr_map_init(nor, sst_mpt);
if (ret)
goto out;

out:
kfree(sst_mpt);
return ret;
}

static const struct spi_nor_manufacturer_sfdp sst_sfdp = {
.id = SFDP_SST_ID,
.parse = sst_manufacturer_specific_sfdp_parse,
};

const struct spi_nor_manufacturer spi_nor_sst = {
.name = "sst",
.parts = sst_parts,
.nparts = ARRAY_SIZE(sst_parts),
.fixups = &sst_fixups,
.sfdp = &sst_sfdp,
};

0 comments on commit 4953dd5

Please sign in to comment.