You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
7.0 KiB
329 lines
7.0 KiB
/* |
|
* Copyright (c) 2020 Peter Bigot Consulting, LLC |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
|
|
#include <sys/types.h> |
|
#include <zephyr/kernel.h> |
|
#include "jesd216.h" |
|
#include "spi_nor.h" |
|
|
|
static bool extract_instr(uint16_t packed, |
|
struct jesd216_instr *res) |
|
{ |
|
bool rv = (res != NULL); |
|
|
|
if (rv) { |
|
res->instr = packed >> 8; |
|
res->mode_clocks = (packed >> 5) & 0x07; |
|
res->wait_states = packed & 0x1F; |
|
} |
|
return rv; |
|
} |
|
|
|
int jesd216_bfp_read_support(const struct jesd216_param_header *php, |
|
const struct jesd216_bfp *bfp, |
|
enum jesd216_mode_type mode, |
|
struct jesd216_instr *res) |
|
{ |
|
int rv = -ENOTSUP; |
|
|
|
switch (mode) { |
|
case JESD216_MODE_044: |
|
if ((php->len_dw >= 15) |
|
&& (sys_le32_to_cpu(bfp->dw10[5]) & BIT(9))) { |
|
rv = 0; |
|
} |
|
break; |
|
case JESD216_MODE_088: |
|
if ((php->len_dw >= 19) |
|
&& (sys_le32_to_cpu(bfp->dw10[9]) & BIT(9))) { |
|
rv = 0; |
|
} |
|
break; |
|
case JESD216_MODE_111: |
|
rv = 0; |
|
break; |
|
case JESD216_MODE_112: |
|
if (sys_le32_to_cpu(bfp->dw1) & BIT(16)) { |
|
uint32_t dw4 = sys_le32_to_cpu(bfp->dw4); |
|
|
|
rv = extract_instr(dw4 >> 0, res); |
|
} |
|
break; |
|
case JESD216_MODE_114: |
|
if (sys_le32_to_cpu(bfp->dw1) & BIT(22)) { |
|
uint32_t dw3 = sys_le32_to_cpu(bfp->dw3); |
|
|
|
rv = extract_instr(dw3 >> 16, res); |
|
} |
|
break; |
|
case JESD216_MODE_118: |
|
if (php->len_dw >= 17) { |
|
uint32_t dw17 = sys_le32_to_cpu(bfp->dw10[7]); |
|
|
|
if ((dw17 >> 24) != 0) { |
|
rv = extract_instr(dw17 >> 16, res); |
|
} |
|
} |
|
break; |
|
case JESD216_MODE_122: |
|
if (sys_le32_to_cpu(bfp->dw1) & BIT(20)) { |
|
uint32_t dw4 = sys_le32_to_cpu(bfp->dw4); |
|
|
|
rv = extract_instr(dw4 >> 16, res); |
|
} |
|
break; |
|
case JESD216_MODE_144: |
|
if (sys_le32_to_cpu(bfp->dw1) & BIT(21)) { |
|
uint32_t dw3 = sys_le32_to_cpu(bfp->dw3); |
|
|
|
rv = extract_instr(dw3 >> 0, res); |
|
} |
|
break; |
|
case JESD216_MODE_188: |
|
if (php->len_dw >= 17) { |
|
uint32_t dw17 = sys_le32_to_cpu(bfp->dw10[7]); |
|
|
|
if ((uint8_t)(dw17 >> 8) != 0) { |
|
rv = extract_instr(dw17 >> 0, res); |
|
} |
|
} |
|
break; |
|
case JESD216_MODE_222: |
|
if (sys_le32_to_cpu(bfp->dw5) & BIT(0)) { |
|
uint32_t dw6 = sys_le32_to_cpu(bfp->dw6); |
|
|
|
rv = extract_instr(dw6 >> 16, res); |
|
} |
|
break; |
|
case JESD216_MODE_444: |
|
if (sys_le32_to_cpu(bfp->dw5) & BIT(4)) { |
|
uint32_t dw7 = sys_le32_to_cpu(bfp->dw7); |
|
|
|
rv = extract_instr(dw7 >> 16, res); |
|
} |
|
break; |
|
/* Not clear how to detect these; they are identified only by |
|
* enable/disable sequences. |
|
*/ |
|
case JESD216_MODE_44D4D: |
|
case JESD216_MODE_888: |
|
case JESD216_MODE_8D8D8D: |
|
break; |
|
default: |
|
rv = -EINVAL; |
|
} |
|
|
|
return rv; |
|
} |
|
|
|
int jesd216_bfp_erase(const struct jesd216_bfp *bfp, |
|
uint8_t idx, |
|
struct jesd216_erase_type *etp) |
|
{ |
|
__ASSERT_NO_MSG((idx > 0) && (idx <= JESD216_NUM_ERASE_TYPES)); |
|
|
|
/* Types 1 and 2 are in dw8, types 3 and 4 in dw9 */ |
|
const uint32_t *dwp = &bfp->dw8 + (idx - 1U) / 2U; |
|
uint32_t dw = sys_le32_to_cpu(*dwp); |
|
|
|
/* Type 2(4) is in the upper half of the value. */ |
|
if ((idx & 0x01) == 0x00) { |
|
dw >>= 16; |
|
} |
|
|
|
/* Extract the exponent and command */ |
|
uint8_t exp = (uint8_t)dw; |
|
uint8_t cmd = (uint8_t)(dw >> 8); |
|
|
|
if (exp == 0) { |
|
return -EINVAL; |
|
} |
|
etp->cmd = cmd; |
|
etp->exp = exp; |
|
return 0; |
|
} |
|
|
|
int jesd216_bfp_erase_type_times(const struct jesd216_param_header *php, |
|
const struct jesd216_bfp *bfp, |
|
uint8_t idx, |
|
uint32_t *typ_ms) |
|
{ |
|
__ASSERT_NO_MSG((idx > 0) && (idx <= JESD216_NUM_ERASE_TYPES)); |
|
|
|
/* DW10 introduced in JESD216A */ |
|
if (php->len_dw < 10) { |
|
return -ENOTSUP; |
|
} |
|
|
|
uint32_t dw10 = sys_le32_to_cpu(bfp->dw10[0]); |
|
|
|
/* Each 7-bit erase time entry has a 5-bit count in the lower |
|
* bits, and a 2-bit unit in the upper bits. The actual count |
|
* is the field content plus one. |
|
* |
|
* The entries start with ET1 at bit 4. The low four bits |
|
* encode a value that is offset and scaled to produce a |
|
* multiplier to convert from typical time to maximum time. |
|
*/ |
|
unsigned int count = 1 + ((dw10 >> (4 + (idx - 1) * 7)) & 0x1F); |
|
unsigned int units = ((dw10 >> (4 + 5 + (idx - 1) * 7)) & 0x03); |
|
unsigned int max_factor = 2 * (1 + (dw10 & 0x0F)); |
|
|
|
switch (units) { |
|
case 0x00: /* 1 ms */ |
|
*typ_ms = count; |
|
break; |
|
case 0x01: /* 16 ms */ |
|
*typ_ms = count * 16; |
|
break; |
|
case 0x02: /* 128 ms */ |
|
*typ_ms = count * 128; |
|
break; |
|
case 0x03: /* 1 s */ |
|
*typ_ms = count * MSEC_PER_SEC; |
|
break; |
|
} |
|
|
|
return max_factor; |
|
} |
|
|
|
int jesd216_bfp_decode_dw11(const struct jesd216_param_header *php, |
|
const struct jesd216_bfp *bfp, |
|
struct jesd216_bfp_dw11 *res) |
|
{ |
|
/* DW11 introduced in JESD216A */ |
|
if (php->len_dw < 11) { |
|
return -ENOTSUP; |
|
} |
|
|
|
uint32_t dw11 = sys_le32_to_cpu(bfp->dw10[1]); |
|
uint32_t value = 1 + ((dw11 >> 24) & 0x1F); |
|
|
|
switch ((dw11 >> 29) & 0x03) { |
|
case 0x00: /* 16 ms */ |
|
value *= 16; |
|
break; |
|
case 0x01: |
|
value *= 256; |
|
break; |
|
case 0x02: |
|
value *= 4 * MSEC_PER_SEC; |
|
break; |
|
case 0x03: |
|
value *= 64 * MSEC_PER_SEC; |
|
break; |
|
} |
|
res->chip_erase_ms = value; |
|
|
|
value = 1 + ((dw11 >> 19) & 0x0F); |
|
if (dw11 & BIT(23)) { |
|
value *= 8; |
|
} |
|
res->byte_prog_addl_us = value; |
|
|
|
value = 1 + ((dw11 >> 14) & 0x0F); |
|
if (dw11 & BIT(18)) { |
|
value *= 8; |
|
} |
|
res->byte_prog_first_us = value; |
|
|
|
value = 1 + ((dw11 >> 8) & 0x01F); |
|
if (dw11 & BIT(13)) { |
|
value *= 64; |
|
} else { |
|
value *= 8; |
|
} |
|
res->page_prog_us = value; |
|
|
|
res->page_size = BIT((dw11 >> 4) & 0x0F); |
|
res->typ_max_factor = 2 * (1 + (dw11 & 0x0F)); |
|
|
|
return 0; |
|
} |
|
|
|
int jesd216_bfp_decode_dw14(const struct jesd216_param_header *php, |
|
const struct jesd216_bfp *bfp, |
|
struct jesd216_bfp_dw14 *res) |
|
{ |
|
/* DW14 introduced in JESD216A */ |
|
if (php->len_dw < 14) { |
|
return -ENOTSUP; |
|
} |
|
|
|
uint32_t dw14 = sys_le32_to_cpu(bfp->dw10[4]); |
|
|
|
if (dw14 & BIT(31)) { |
|
return -ENOTSUP; |
|
} |
|
|
|
res->enter_dpd_instr = (dw14 >> 23) & 0xFF; |
|
res->exit_dpd_instr = (dw14 >> 15) & 0xFF; |
|
|
|
uint32_t value = 1 + ((dw14 >> 8) & 0x1F); |
|
|
|
switch ((dw14 >> 13) & 0x03) { |
|
case 0x00: /* 128 ns */ |
|
value *= 128; |
|
break; |
|
case 0x01: /* 1 us */ |
|
value *= NSEC_PER_USEC; |
|
break; |
|
case 0x02: /* 8 us */ |
|
value *= 8 * NSEC_PER_USEC; |
|
break; |
|
case 0x03: /* 64 us */ |
|
value *= 64 * NSEC_PER_USEC; |
|
break; |
|
} |
|
|
|
res->exit_delay_ns = value; |
|
|
|
res->poll_options = (dw14 >> 2) & 0x3F; |
|
|
|
return 0; |
|
} |
|
|
|
int jesd216_bfp_decode_dw15(const struct jesd216_param_header *php, |
|
const struct jesd216_bfp *bfp, |
|
struct jesd216_bfp_dw15 *res) |
|
{ |
|
/* DW15 introduced in JESD216A */ |
|
if (php->len_dw < 15) { |
|
return -ENOTSUP; |
|
} |
|
|
|
uint32_t dw15 = sys_le32_to_cpu(bfp->dw10[5]); |
|
|
|
res->hold_reset_disable = (dw15 & BIT(23)) != 0U; |
|
res->qer = (dw15 >> 20) & 0x07; |
|
res->entry_044 = (dw15 >> 16) & 0x0F; |
|
res->exit_044 = (dw15 >> 10) & 0x3F; |
|
res->support_044 = (dw15 & BIT(9)) != 0U; |
|
res->enable_444 = (dw15 >> 4) & 0x1F; |
|
res->disable_444 = (dw15 >> 0) & 0x0F; |
|
|
|
return 0; |
|
} |
|
|
|
int jesd216_bfp_decode_dw16(const struct jesd216_param_header *php, |
|
const struct jesd216_bfp *bfp, |
|
struct jesd216_bfp_dw16 *res) |
|
{ |
|
/* DW16 introduced in JESD216A */ |
|
if (php->len_dw < 16) { |
|
return -ENOTSUP; |
|
} |
|
|
|
uint32_t dw16 = sys_le32_to_cpu(bfp->dw10[6]); |
|
|
|
res->enter_4ba = (dw16 >> 24) & 0xFF; |
|
res->exit_4ba = (dw16 >> 14) & 0x3FF; |
|
res->srrs_support = (dw16 >> 8) & 0x3F; |
|
res->sr1_interface = (dw16 >> 0) & 0x7F; |
|
|
|
return 0; |
|
}
|
|
|