samples: drivers: jesd216: support JESD216 API
Demonstrate use of the JESD216 API as a utility to display the discoverable parameters. Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
parent
368cf2fc45
commit
506f02e89e
10
samples/drivers/jesd216/CMakeLists.txt
Normal file
10
samples/drivers/jesd216/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(flash_jesd216)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers/flash)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
64
samples/drivers/jesd216/README.rst
Normal file
64
samples/drivers/jesd216/README.rst
Normal file
|
@ -0,0 +1,64 @@
|
|||
.. jesd216-sample:
|
||||
|
||||
JESD216 Sample
|
||||
##############
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample demonstrates how to use the JESD216 flash API to extract
|
||||
information from a compatible serial device, and serves as utility to
|
||||
generate ``jedec,spi-nor`` devicetree property values for the device.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The application will build only for a target that has a devicetree entry
|
||||
with ``jedec,spi-nor`` as a compatible, and for which the driver
|
||||
supports :option:`CONFIG_FLASH_JESD216_API`.
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/drivers/jesd216
|
||||
:board: efr32mg_sltb004a
|
||||
:goals: build flash
|
||||
:compact:
|
||||
|
||||
Sample Output
|
||||
=============
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
MX25R8035F: SFDP v 1.6 AP ff with 2 PH
|
||||
PH0: ff00 rev 1.6: 16 DW @ 30
|
||||
Summary of BFP content:
|
||||
DTR Clocking not supported
|
||||
Addressing: 3-Byte only
|
||||
4-KiBy erase: uniform
|
||||
Support QSPI XIP
|
||||
Support 1-1-1
|
||||
Support 1-1-2: instr 3Bh, 0 mode clocks, 8 waits
|
||||
Support 1-1-4: instr 6Bh, 0 mode clocks, 8 waits
|
||||
Support 1-2-2: instr BBh, 0 mode clocks, 4 waits
|
||||
Support 1-4-4: instr EBh, 2 mode clocks, 4 waits
|
||||
Flash density: 1048576 bytes
|
||||
ET1: instr 20h for 4096 By; typ 48 ms, max 384 ms
|
||||
ET2: instr 52h for 32768 By; typ 240 ms, max 1920 ms
|
||||
ET3: instr D8h for 65536 By; typ 480 ms, max 3840 ms
|
||||
Chip erase: typ 5120 ms, max 30720 ms
|
||||
Byte program: type 32 + 1 * B us, max 192 + 6 * B us
|
||||
Page program: typ 896 us, max 5376 us
|
||||
Page size: 256 By
|
||||
Suspend: B0h ; Resume: 30h
|
||||
DPD: Enter B9h, exit ABh ; delay 40000 ns ; poll 0x3d
|
||||
size = <8388608>;
|
||||
sfdp-bfp = [
|
||||
e5 20 f1 ff ff ff 7f 00 44 eb 08 6b 08 3b 04 bb
|
||||
ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52
|
||||
10 d8 00 ff 23 72 f5 00 82 ed 04 b7 44 83 38 44
|
||||
30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff
|
||||
];
|
||||
PH1: ffc2 rev 1.0: 4 DW @ 110
|
||||
sfdp-ffc2 = [
|
||||
00 36 50 16 9d f9 c0 64 fe cf ff ff ff ff ff ff
|
||||
];
|
||||
jedec-id = [c2 28 14];
|
14
samples/drivers/jesd216/boards/nrf52840dk_nrf52840.conf
Normal file
14
samples/drivers/jesd216/boards/nrf52840dk_nrf52840.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# Need this when storage is on MX25R64
|
||||
#CONFIG_NORDIC_QSPI_NOR=y
|
||||
#CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_NOR=y
|
||||
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
||||
CONFIG_SPI_2=y
|
52
samples/drivers/jesd216/boards/nrf52840dk_nrf52840.overlay
Normal file
52
samples/drivers/jesd216/boards/nrf52840dk_nrf52840.overlay
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/delete-node/ &storage_partition;
|
||||
/delete-node/ &mx25r64;
|
||||
|
||||
&qspi {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&spi2 {
|
||||
status = "okay";
|
||||
cs-gpios = <&gpio0 17 0>, <&gpio1 5 0>;
|
||||
mx25r64: mx25r6435f@0 {
|
||||
status = "okay";
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <8000000>;
|
||||
label = "MX25R64";
|
||||
jedec-id = [c2 28 17];
|
||||
sfdp-bfp = [
|
||||
e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb
|
||||
ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52
|
||||
10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44
|
||||
30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff
|
||||
];
|
||||
|
||||
size = <67108864>;
|
||||
has-be32k;
|
||||
has-dpd;
|
||||
t-enter-dpd = <10000>;
|
||||
dpd-wakeup-sequence = <30000 20 45000>;
|
||||
wp-gpios = <&gpio0 22 0>;
|
||||
hold-gpios = <&gpio0 23 0>;
|
||||
};
|
||||
};
|
||||
|
||||
&mx25r64 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "storage";
|
||||
reg = <0x00000000 0x00010000>;
|
||||
};
|
||||
};
|
||||
};
|
8
samples/drivers/jesd216/prj.conf
Normal file
8
samples/drivers/jesd216/prj.conf
Normal file
|
@ -0,0 +1,8 @@
|
|||
CONFIG_STDOUT_CONSOLE=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_JESD216_API=y
|
||||
|
||||
# Assume the standard SPI NOR flash driver. If the device uses
|
||||
# another driver add an override configuration in boards/.
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_NOR=y
|
14
samples/drivers/jesd216/sample.yaml
Normal file
14
samples/drivers/jesd216/sample.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
sample:
|
||||
name: JESD216 Sample
|
||||
tests:
|
||||
sample.drivers.jesd216:
|
||||
tags: spi flash
|
||||
filter: dt_compat_enabled("jedec,spi-nor")
|
||||
harness: console
|
||||
harness_config:
|
||||
type: multi_line
|
||||
ordered: true
|
||||
regex:
|
||||
- "sfdp-bfp ="
|
||||
- "jedec-id ="
|
||||
depends_on: spi
|
297
samples/drivers/jesd216/src/main.c
Normal file
297
samples/drivers/jesd216/src/main.c
Normal file
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Peter Bigot Consulting, LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <device.h>
|
||||
#include <drivers/flash.h>
|
||||
#include <jesd216.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#if DT_NODE_HAS_STATUS(DT_INST(0, jedec_spi_nor), okay)
|
||||
#define FLASH_DEVICE DT_LABEL(DT_INST(0, jedec_spi_nor))
|
||||
#else
|
||||
#error Unsupported flash driver
|
||||
#define FLASH_DEVICE ""
|
||||
#endif
|
||||
|
||||
typedef void (*dw_extractor)(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp);
|
||||
|
||||
static const char * const mode_tags[] = {
|
||||
[JESD216_MODE_044] = "QSPI XIP",
|
||||
[JESD216_MODE_088] = "OSPI XIP",
|
||||
[JESD216_MODE_111] = "1-1-1",
|
||||
[JESD216_MODE_112] = "1-1-2",
|
||||
[JESD216_MODE_114] = "1-1-4",
|
||||
[JESD216_MODE_118] = "1-1-8",
|
||||
[JESD216_MODE_122] = "1-2-2",
|
||||
[JESD216_MODE_144] = "1-4-4",
|
||||
[JESD216_MODE_188] = "1-8-8",
|
||||
[JESD216_MODE_222] = "2-2-2",
|
||||
[JESD216_MODE_444] = "4-4-4",
|
||||
};
|
||||
|
||||
static void summarize_dw1(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
uint32_t dw1 = sys_le32_to_cpu(bfp->dw1);
|
||||
|
||||
printf("DTR Clocking %ssupported\n",
|
||||
(dw1 & JESD216_SFDP_BFP_DW1_DTRCLK_FLG) ? "" : "not ");
|
||||
|
||||
static const char *const addr_bytes[] = {
|
||||
[JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B] = "3-Byte only",
|
||||
[JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B4B] = "3- or 4-Byte",
|
||||
[JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_4B] = "4-Byte only",
|
||||
[3] = "Reserved",
|
||||
};
|
||||
|
||||
printf("Addressing: %s\n", addr_bytes[(dw1 & JESD216_SFDP_BFP_DW1_ADDRBYTES_MASK)
|
||||
>> JESD216_SFDP_BFP_DW1_ADDRBYTES_SHFT]);
|
||||
|
||||
static const char *const bsersz[] = {
|
||||
[0] = "Reserved 00b",
|
||||
[JESD216_SFDP_BFP_DW1_BSERSZ_VAL_4KSUP] = "uniform",
|
||||
[2] = "Reserved 01b",
|
||||
[JESD216_SFDP_BFP_DW1_BSERSZ_VAL_4KNOTSUP] = "not uniform",
|
||||
};
|
||||
|
||||
printf("4-KiBy erase: %s\n", bsersz[(dw1 & JESD216_SFDP_BFP_DW1_BSERSZ_MASK)
|
||||
>> JESD216_SFDP_BFP_DW1_BSERSZ_SHFT]);
|
||||
|
||||
for (size_t mode = 0; mode < ARRAY_SIZE(mode_tags); ++mode) {
|
||||
const char *tag = mode_tags[mode];
|
||||
|
||||
if (tag) {
|
||||
struct jesd216_instr cmd;
|
||||
int rc = jesd216_bfp_read_support(php, bfp,
|
||||
(enum jesd216_mode_type)mode,
|
||||
&cmd);
|
||||
|
||||
if (rc == 0) {
|
||||
printf("Support %s\n", tag);
|
||||
} else if (rc > 0) {
|
||||
printf("Support %s: instr %02Xh, %u mode clocks, %u waits\n",
|
||||
tag, cmd.instr, cmd.mode_clocks, cmd.wait_states);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void summarize_dw2(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
printf("Flash density: %u bytes\n", (uint32_t)(jesd216_bfp_density(bfp) / 8));
|
||||
}
|
||||
|
||||
static void summarize_dw89(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
struct jesd216_erase_type etype;
|
||||
uint32_t typ_ms;
|
||||
int typ_max_mul;
|
||||
|
||||
for (uint8_t idx = 1; idx < JESD216_NUM_ERASE_TYPES; ++idx) {
|
||||
if (jesd216_bfp_erase(bfp, idx, &etype) == 0) {
|
||||
typ_max_mul = jesd216_bfp_erase_type_times(php, bfp,
|
||||
idx, &typ_ms);
|
||||
|
||||
printf("ET%u: instr %02Xh for %u By", idx, etype.cmd,
|
||||
(uint32_t)BIT(etype.exp));
|
||||
if (typ_max_mul > 0) {
|
||||
printf("; typ %u ms, max %u ms",
|
||||
typ_ms, typ_max_mul * typ_ms);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void summarize_dw11(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
struct jesd216_bfp_dw11 dw11;
|
||||
|
||||
if (jesd216_bfp_decode_dw11(php, bfp, &dw11) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Chip erase: typ %u ms, max %u ms\n",
|
||||
dw11.chip_erase_ms, dw11.typ_max_factor * dw11.chip_erase_ms);
|
||||
|
||||
printf("Byte program: type %u + %u * B us, max %u + %u * B us\n",
|
||||
dw11.byte_prog_first_us, dw11.byte_prog_addl_us,
|
||||
dw11.typ_max_factor * dw11.byte_prog_first_us,
|
||||
dw11.typ_max_factor * dw11.byte_prog_addl_us);
|
||||
|
||||
printf("Page program: typ %u us, max %u us\n",
|
||||
dw11.page_prog_us,
|
||||
dw11.typ_max_factor * dw11.page_prog_us);
|
||||
|
||||
printf("Page size: %u By\n", dw11.page_size);
|
||||
}
|
||||
|
||||
static void summarize_dw12(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
uint32_t dw12 = sys_le32_to_cpu(bfp->dw10[2]);
|
||||
uint32_t dw13 = sys_le32_to_cpu(bfp->dw10[3]);
|
||||
|
||||
/* Inverted logic flag: 1 means not supported */
|
||||
if ((dw12 & JESD216_SFDP_BFP_DW12_SUSPRESSUP_FLG) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t susp_instr = dw13 >> 24;
|
||||
uint8_t resm_instr = dw13 >> 16;
|
||||
uint8_t psusp_instr = dw13 >> 8;
|
||||
uint8_t presm_instr = dw13 >> 0;
|
||||
|
||||
printf("Suspend: %02Xh ; Resume: %02Xh\n",
|
||||
susp_instr, resm_instr);
|
||||
if ((susp_instr != psusp_instr)
|
||||
|| (resm_instr != presm_instr)) {
|
||||
printf("Program suspend: %02Xh ; Resume: %02Xh\n",
|
||||
psusp_instr, presm_instr);
|
||||
}
|
||||
}
|
||||
|
||||
static void summarize_dw14(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
struct jesd216_bfp_dw14 dw14;
|
||||
|
||||
if (jesd216_bfp_decode_dw14(php, bfp, &dw14) != 0) {
|
||||
return;
|
||||
}
|
||||
printf("DPD: Enter %02Xh, exit %02Xh ; delay %u ns ; poll 0x%02x\n",
|
||||
dw14.enter_dpd_instr, dw14.exit_dpd_instr,
|
||||
dw14.exit_delay_ns, dw14.poll_options);
|
||||
}
|
||||
|
||||
/* Indexed from 1 to match JESD216 data word numbering */
|
||||
static const dw_extractor extractor[] = {
|
||||
[1] = summarize_dw1,
|
||||
[2] = summarize_dw2,
|
||||
[8] = summarize_dw89,
|
||||
[11] = summarize_dw11,
|
||||
[12] = summarize_dw12,
|
||||
[14] = summarize_dw14,
|
||||
};
|
||||
|
||||
static void dump_bfp(const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
uint8_t dw = 1;
|
||||
uint8_t limit = MIN(php->len_dw, ARRAY_SIZE(extractor));
|
||||
|
||||
printf("Summary of BFP content:\n");
|
||||
while (dw < limit) {
|
||||
dw_extractor ext = extractor[dw];
|
||||
|
||||
if (ext != 0) {
|
||||
ext(php, bfp);
|
||||
}
|
||||
++dw;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_bytes(const struct jesd216_param_header *php,
|
||||
const uint32_t *dw)
|
||||
{
|
||||
char buffer[4 * 3 + 1]; /* B1 B2 B3 B4 */
|
||||
uint8_t nw = 0;
|
||||
|
||||
printf(" [\n\t");
|
||||
while (nw < php->len_dw) {
|
||||
const uint8_t *u8p = (const uint8_t *)&dw[nw];
|
||||
++nw;
|
||||
|
||||
bool emit_nl = (nw == php->len_dw) || ((nw % 4) == 0);
|
||||
|
||||
sprintf(buffer, "%02x %02x %02x %02x",
|
||||
u8p[0], u8p[1], u8p[2], u8p[3]);
|
||||
if (emit_nl) {
|
||||
printf("%s\n\t", buffer);
|
||||
} else {
|
||||
printf("%s ", buffer);
|
||||
}
|
||||
}
|
||||
printf("];\n");
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
struct device *dev = device_get_binding(FLASH_DEVICE);
|
||||
|
||||
if (!dev) {
|
||||
printf("%s: device not found\n", FLASH_DEVICE);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t decl_nph = 5;
|
||||
union {
|
||||
uint8_t raw[JESD216_SFDP_SIZE(decl_nph)];
|
||||
struct jesd216_sfdp_header sfdp;
|
||||
} u;
|
||||
const struct jesd216_sfdp_header *hp = &u.sfdp;
|
||||
int rc = flash_sfdp_read(dev, 0, u.raw, sizeof(u.raw));
|
||||
uint32_t magic = jesd216_sfdp_magic(hp);
|
||||
|
||||
if (magic != JESD216_SFDP_MAGIC) {
|
||||
printf("SFDP magic %08x invalid", magic);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s: SFDP v %u.%u AP %x with %u PH\n", dev->name,
|
||||
hp->rev_major, hp->rev_minor, hp->access, hp->nph);
|
||||
|
||||
const struct jesd216_param_header *php = hp->phdr;
|
||||
const struct jesd216_param_header *phpe = php + MIN(decl_nph, hp->nph);
|
||||
|
||||
while (php != phpe) {
|
||||
uint16_t id = jesd216_param_id(php);
|
||||
uint32_t addr = jesd216_param_addr(php);
|
||||
|
||||
printf("PH%u: %04x rev %u.%u: %u DW @ %x\n",
|
||||
(php - hp->phdr), id, php->rev_major, php->rev_minor,
|
||||
php->len_dw, addr);
|
||||
|
||||
uint32_t dw[php->len_dw];
|
||||
|
||||
rc = flash_sfdp_read(dev, addr, dw, sizeof(dw));
|
||||
if (rc != 0) {
|
||||
printf("Read failed: %d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == JESD216_SFDP_PARAM_ID_BFP) {
|
||||
const struct jesd216_bfp *bfp = (struct jesd216_bfp *)dw;
|
||||
|
||||
dump_bfp(php, bfp);
|
||||
printf("size = <%u>;\n", (uint32_t)jesd216_bfp_density(bfp));
|
||||
printf("sfdp-bfp =");
|
||||
} else {
|
||||
printf("sfdp-%04x =", id);
|
||||
}
|
||||
|
||||
dump_bytes(php, dw);
|
||||
|
||||
++php;
|
||||
}
|
||||
|
||||
uint8_t id[3];
|
||||
|
||||
rc = flash_read_jedec_id(dev, id);
|
||||
if (rc == 0) {
|
||||
printf("jedec-id = [%02x %02x %02x];\n",
|
||||
id[0], id[1], id[2]);
|
||||
} else {
|
||||
printf("JEDEC ID read failed: %d\n", rc);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue