Skip to content

Commit

Permalink
mtd: rawnand: Add support for fixed patterns
Browse files Browse the repository at this point in the history
Some controllers are so specialized they implement high-level operations
like read/write page without allowing for customized op codes, length
or number of address cycles. This is limiting us quite a bit, but we
have to support those hardware controllers nonetheless, so let's extend
the parser logic to allow describing such fixed patterns.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
  • Loading branch information
bbrezillon committed May 2, 2020
1 parent 0050f38 commit 30248fb
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 6 deletions.
73 changes: 70 additions & 3 deletions drivers/mtd/nand/raw/nand_base.c
Expand Up @@ -1308,7 +1308,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
instrs);
int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
int ret;
u8 status;
Expand Down Expand Up @@ -1695,7 +1696,8 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
PSEC_TO_MSEC(sdr->tWB_max)),
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
instrs);

if (chip->options & NAND_ROW_ADDR_3)
instrs[1].ctx.addr.naddrs++;
Expand Down Expand Up @@ -2024,6 +2026,61 @@ nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat,
return false;
}

/**
* nand_op_parser_pat_elem_matches() - Checks if a pattern element matches an
* instruction
* @elem: pattern element to check
* @instruction: NAND instruction to check against
* @instr_offset: offset withing the instruction in case of address or data
* instructions
*/
static bool
nand_op_parser_pat_elem_matches(const struct nand_op_parser_pattern_elem *elem,
const struct nand_op_instr *instr,
unsigned int instr_offset)
{
unsigned int len;

if (instr->type != elem->type)
return false;

switch (instr->type) {
case NAND_OP_CMD_INSTR:
if (elem->ctx.cmd.fixed &&
elem->ctx.cmd.opcode != instr->ctx.cmd.opcode)
return false;
break;

case NAND_OP_ADDR_INSTR:
/*
* No need to check max cycles here, as we try to split
* operations if they're longer than what the hardware
* supports.
*/
len = instr->ctx.addr.naddrs - instr_offset;
if (len < elem->ctx.addr.mincycles)
return false;
break;

case NAND_OP_DATA_IN_INSTR:
case NAND_OP_DATA_OUT_INSTR:
/*
* No need to check max data len here, as we try to split
* operations if they're longer than what the hardware
* supports.
*/
len = instr->ctx.data.len - instr_offset;
if (len < elem->ctx.data.minlen)
return false;
break;

default:
break;
}

return true;
}

/**
* nand_op_parser_match_pat - Checks if a pattern matches the instructions
* remaining in the parser context
Expand Down Expand Up @@ -2052,7 +2109,17 @@ nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat,
* to the next one. If the element is mandatory, there's no
* match and we can return false directly.
*/
if (instr->type != pat->elems[i].type) {
if (nand_op_parser_pat_elem_matches(&pat->elems[i], instr,
instr_offset)) {
if (!pat->elems[i].optional)
return false;

continue;
}

if (instr->type == NAND_OP_CMD_INSTR &&
pat->elems[i].ctx.cmd.fixed &&
pat->elems[i].ctx.cmd.opcode != instr->ctx.cmd.opcode) {
if (!pat->elems[i].optional)
return false;

Expand Down
61 changes: 58 additions & 3 deletions include/linux/mtd/rawnand.h
Expand Up @@ -736,20 +736,36 @@ unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop,
unsigned int nand_subop_get_data_len(const struct nand_subop *subop,
unsigned int op_id);


/**
* struct nand_op_parser_cmd_constraints - Constraints for command instructions
* @fixed: when set to true the opcode is fixed and must match the value in
* @opcode
* @opcode: fixed opcode to match against
*/
struct nand_op_parser_cmd_constraints {
bool fixed;
u8 opcode;
};

/**
* struct nand_op_parser_addr_constraints - Constraints for address instructions
* @mincycles: minimum number of address cycles the controller can issue
* @maxcycles: maximum number of address cycles the controller can issue in a
* single step
*/
struct nand_op_parser_addr_constraints {
unsigned int mincycles;
unsigned int maxcycles;
};

/**
* struct nand_op_parser_data_constraints - Constraints for data instructions
* @minlen: minimum number of data cycles the controller can issue
* @maxlen: maximum data length that the controller can handle in a single step
*/
struct nand_op_parser_data_constraints {
unsigned int minlen;
unsigned int maxlen;
};

Expand All @@ -765,6 +781,7 @@ struct nand_op_parser_pattern_elem {
enum nand_op_instr_type type;
bool optional;
union {
struct nand_op_parser_cmd_constraints cmd;
struct nand_op_parser_addr_constraints addr;
struct nand_op_parser_data_constraints data;
} ctx;
Expand All @@ -776,27 +793,65 @@ struct nand_op_parser_pattern_elem {
.optional = _opt, \
}

#define NAND_OP_PARSER_PAT_ADDR_ELEM(_opt, _maxcycles) \
#define NAND_OP_PARSER_PAT_FIXED_CMD_ELEM(_opt, _opcode) \
{ \
.type = NAND_OP_CMD_INSTR, \
.optional = _opt, \
.ctx.cmd.fixed = true, \
.ctx.cmd.opcode = _opcode, \
}

#define NAND_OP_PARSER_PAT_MIN_MAX_ADDR_ELEM(_opt, _mincycles, \
_maxcycles) \
{ \
.type = NAND_OP_ADDR_INSTR, \
.optional = _opt, \
.ctx.addr.mincycles = _mincycles, \
.ctx.addr.maxcycles = _maxcycles, \
}

#define NAND_OP_PARSER_PAT_DATA_IN_ELEM(_opt, _maxlen) \
#define NAND_OP_PARSER_PAT_ADDR_ELEM(_opt, _maxcycles) \
NAND_OP_PARSER_PAT_MIN_MAX_ADDR_ELEM(_opt, 0, \
_maxcycles)

#define NAND_OP_PARSER_PAT_FIXED_ADDR_ELEM(_opt, _numcycles) \
NAND_OP_PARSER_PAT_MIN_MAX_ADDR_ELEM(_opt, _numcycles, \
_numcycles)

#define NAND_OP_PARSER_PAT_MIN_MAX_DATA_IN_ELEM(_opt, _minlen, \
_maxlen) \
{ \
.type = NAND_OP_DATA_IN_INSTR, \
.optional = _opt, \
.ctx.data.minlen = _minlen, \
.ctx.data.maxlen = _maxlen, \
}

#define NAND_OP_PARSER_PAT_DATA_OUT_ELEM(_opt, _maxlen) \
#define NAND_OP_PARSER_PAT_DATA_IN_ELEM(_opt, _maxlen) \
NAND_OP_PARSER_PAT_MIN_MAX_DATA_IN_ELEM(_opt, 0, \
_maxlen)

#define NAND_OP_PARSER_PAT_FIXED_DATA_IN_ELEM(_opt, _len) \
NAND_OP_PARSER_PAT_MIN_MAX_DATA_IN_ELEM(_opt, _len, \
_len)

#define NAND_OP_PARSER_PAT_MIN_MAX_DATA_OUT_ELEM(_opt, _minlen, \
_maxlen) \
{ \
.type = NAND_OP_DATA_OUT_INSTR, \
.optional = _opt, \
.ctx.data.minlen = _minlen, \
.ctx.data.maxlen = _maxlen, \
}

#define NAND_OP_PARSER_PAT_DATA_OUT_ELEM(_opt, _maxlen) \
NAND_OP_PARSER_PAT_MIN_MAX_DATA_OUT_ELEM(_opt, 0, \
_maxlen)

#define NAND_OP_PARSER_PAT_FIXED_DATA_OUT_ELEM(_opt, _len) \
NAND_OP_PARSER_PAT_MIN_MAX_DATA_OUT_ELEM(_opt, _len, \
_len)

#define NAND_OP_PARSER_PAT_WAITRDY_ELEM(_opt) \
{ \
.type = NAND_OP_WAITRDY_INSTR, \
Expand Down

0 comments on commit 30248fb

Please sign in to comment.