| /* |
| * 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; |
| } |