/* * Copyright (c) 2020 Peter Bigot Consulting, LLC * * SPDX-License-Identifier: Apache-2.0 */ #include #include #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 * multipler 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; }