[PATCH 4/4] mtd: nand: raw: atmel: Introduce optional debug commands
Eugen Hristev
eugen.hristev at collabora.com
Thu Mar 7 10:59:37 CET 2024
On 3/7/24 11:10, Alexander Dahl wrote:
> For now adds one new command 'hsmc' with a single subcommand 'decode' to
> read and display the content of the registers of the Static Memory
> Controllers (SMC/HSMC) found in different at91 SoCs. Needed to get a
> better picture on what raw nand core and atmel nand controller driver
> try to set as timings based on ONFI parameters of the connected NAND
> chip.
>
> Tested on SAMA5D2 and SAM9X60 based boards. Example output:
>
> U-Boot> hsmc decode
>
> mck clock rate: 200000000
>
> SMC_SETUP3: 0x00000002
> SMC_PULSE3: 0x07040703
> SMC_CYCLE3: 0x00070007
> SMC_MODE3: 0x001f0003
> NCS_RD: setup: 0 (0 ns), pulse: 7 (35 ns), hold: 0 (0 ns), cycle: 7 (35 ns)
> NRD: setup: 0 (0 ns), pulse: 4 (20 ns), hold: 3 (15 ns), cycle: 7 (35 ns)
> NCS_WR: setup: 0 (0 ns), pulse: 7 (35 ns), hold: 0 (0 ns), cycle: 7 (35 ns)
> NWE: setup: 2 (10 ns), pulse: 3 (15 ns), hold: 2 (10 ns), cycle: 7 (35 ns)
> Standard read is applied.
> TDF optimization enabled
> TDF cycles: 15 (75 ns)
> Data Bus Width: 8-bit bus
> NWAIT Mode: 0
> Write operation controlled by NWE signal
> Read operation controlled by NRD signal
Adding Mihai as he is usually very interested in such debug information and methods.
>
> Signed-off-by: Alexander Dahl <ada at thorsis.com>
> ---
> drivers/mtd/nand/raw/Kconfig | 9 +
> drivers/mtd/nand/raw/atmel/nand-controller.c | 249 +++++++++++++++++++
> 2 files changed, 258 insertions(+)
>
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index f6644899b0a..43057aa6c5b 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -50,12 +50,21 @@ config SYS_NAND_NO_SUBPAGE_WRITE
>
> config DM_NAND_ATMEL
> bool "Support Atmel NAND controller with DM support"
> + select MFD_ATMEL_SMC
> select SYS_NAND_SELF_INIT
> imply SYS_NAND_USE_FLASH_BBT
> help
> Enable this driver for NAND flash platforms using an Atmel NAND
> controller.
>
> +config CMD_NAND_ATMEL_DEBUG
> + bool "Optional debug commands for Atmel NAND controller"
> + depends on DM_NAND_ATMEL
> + help
> + Add commands for debugging internals of the Atmel NAND flash
> + controller, for example:
> + - Decode Static Memory Controller (SMC) registers
> +
> config NAND_ATMEL
> bool "Support Atmel NAND controller"
> select SYS_NAND_SELF_INIT
> diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
> index e06523f3298..052d9c7b82a 100644
> --- a/drivers/mtd/nand/raw/atmel/nand-controller.c
> +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
> @@ -51,11 +51,13 @@
>
> #include <asm-generic/gpio.h>
> #include <clk.h>
> +#include <command.h>
> #include <dm/device_compat.h>
> #include <dm/devres.h>
> #include <dm/of_addr.h>
> #include <dm/of_access.h>
> #include <dm/uclass.h>
> +#include <linux/bitops.h>
> #include <linux/completion.h>
> #include <linux/io.h>
> #include <linux/iopoll.h>
> @@ -69,6 +71,7 @@
> #include <nand.h>
> #include <regmap.h>
> #include <syscon.h>
> +#include <vsprintf.h>
>
> #include "pmecc.h"
>
> @@ -216,6 +219,7 @@ struct atmel_nand_controller_ops {
> int (*ecc_init)(struct nand_chip *chip);
> int (*setup_data_interface)(struct atmel_nand *nand, int csline,
> const struct nand_data_interface *conf);
> + void (*print_info)(struct atmel_nand *nand, int csline);
> };
>
> struct atmel_nand_controller_caps {
> @@ -2041,12 +2045,214 @@ err:
> return ret;
> }
>
> +#ifdef CONFIG_CMD_NAND_ATMEL_DEBUG
> +u32 atmel_smc_decode_ncycles(u32 reg, u32 shift, u32 msbpos, u32 msbwidth, u32 msbfactor)
> +{
> + /*
> + * Examples:
> + *
> + * NRD setup length = (128 * NRD_SETUP[5] + NRD_SETUP[4:0]) clock cycles.
> + * NRD pulse length = (256 * NRD_PULSE[6] + NRD_PULSE[5:0]) clock cycles.
> + * Read cycle length = (NRD_CYCLE[8:7] * 256) + NRD_CYCLE[6:0] clock cycles.
> + */
> +
> + reg >>= shift;
> +
> + u32 lsbmask = GENMASK(msbpos - 1, 0);
> + u32 msbmask = GENMASK(msbwidth - 1, 0) << msbpos;
> + u32 msb = (reg & msbmask) >> msbpos;
> + u32 lsb = (reg & lsbmask);
> +
> + return msb * msbfactor + lsb;
> +}
> +
> +static void atmel_smc_cs_conf_print_raw(struct atmel_smc_cs_conf *conf, int cs)
> +{
> + printf("SMC_SETUP%d: 0x%08x\n", cs, conf->setup);
> + printf("SMC_PULSE%d: 0x%08x\n", cs, conf->pulse);
> + printf("SMC_CYCLE%d: 0x%08x\n", cs, conf->cycle);
> + printf("SMC_MODE%d: 0x%08x\n", cs, conf->mode);
> +}
> +
> +static void atmel_hsmc_cs_conf_print_raw(struct atmel_smc_cs_conf *conf, int cs)
> +{
> + printf("HSMC_SETUP%d: 0x%08x\n", cs, conf->setup);
> + printf("HSMC_PULSE%d: 0x%08x\n", cs, conf->pulse);
> + printf("HSMC_CYCLE%d: 0x%08x\n", cs, conf->cycle);
> + printf("HSMC_TIMINGS%d: 0x%08x\n", cs, conf->timings);
> + printf("HSMC_MODE%d: 0x%08x\n", cs, conf->mode);
> +}
> +
> +static void atmel_smc_print_reg(const char *name, u32 setup, u32 pulse,
> + u32 cycle, u32 clk_period_ns)
> +{
> + u32 hold = cycle - pulse - setup;
> +
> + printf("%6s: setup: %u (%u ns), pulse: %u (%u ns), hold: %u (%u ns), cycle: %u (%u ns)\n",
> + name, setup, setup * clk_period_ns, pulse, pulse * clk_period_ns,
> + hold, hold * clk_period_ns, cycle, cycle * clk_period_ns);
> +}
> +
> +static void atmel_smc_print_ncs_rd(struct atmel_smc_cs_conf *conf, u32 clk_period_ns)
> +{
> + u32 ncs_rd_setup = atmel_smc_decode_ncycles(conf->setup, 24, 5, 1, 128);
> + u32 ncs_rd_pulse = atmel_smc_decode_ncycles(conf->pulse, 24, 6, 1, 256);
> + u32 nrd_cycle = atmel_smc_decode_ncycles(conf->cycle, 16, 7, 2, 256);
> +
> + atmel_smc_print_reg("NCS_RD", ncs_rd_setup, ncs_rd_pulse,
> + nrd_cycle, clk_period_ns);
> +}
> +
> +static void atmel_smc_print_nrd(struct atmel_smc_cs_conf *conf, u32 clk_period_ns)
> +{
> + u32 nrd_setup = atmel_smc_decode_ncycles(conf->setup, 16, 5, 1, 128);
> + u32 nrd_pulse = atmel_smc_decode_ncycles(conf->pulse, 16, 6, 1, 256);
> + u32 nrd_cycle = atmel_smc_decode_ncycles(conf->cycle, 16, 7, 2, 256);
> +
> + atmel_smc_print_reg("NRD", nrd_setup, nrd_pulse, nrd_cycle, clk_period_ns);
> +}
> +
> +static void atmel_smc_print_ncs_wr(struct atmel_smc_cs_conf *conf, u32 clk_period_ns)
> +{
> + u32 ncs_wr_setup = atmel_smc_decode_ncycles(conf->setup, 8, 5, 1, 128);
> + u32 ncs_wr_pulse = atmel_smc_decode_ncycles(conf->pulse, 8, 6, 1, 256);
> + u32 nwe_cycle = atmel_smc_decode_ncycles(conf->cycle, 0, 7, 2, 256);
> +
> + atmel_smc_print_reg("NCS_WR", ncs_wr_setup, ncs_wr_pulse,
> + nwe_cycle, clk_period_ns);
> +}
> +
> +static void atmel_smc_print_nwe(struct atmel_smc_cs_conf *conf, u32 clk_period_ns)
> +{
> + u32 nwe_setup = atmel_smc_decode_ncycles(conf->setup, 0, 5, 1, 128);
> + u32 nwe_pulse = atmel_smc_decode_ncycles(conf->pulse, 0, 6, 1, 256);
> + u32 nwe_cycle = atmel_smc_decode_ncycles(conf->cycle, 0, 7, 2, 256);
> +
> + atmel_smc_print_reg("NWE", nwe_setup, nwe_pulse, nwe_cycle, clk_period_ns);
> +}
> +
> +static void atmel_smc_print_mode(struct atmel_smc_cs_conf *conf, u32 clk_period_ns)
> +{
> + u32 tdf;
> + u8 dbw;
> +
> + if (conf->mode & BIT(24)) {
> + printf("Asynchronous burst read in Page mode is applied on the corresponding chip select.\n");
> + printf("Page Size: %u-byte page\n",
> + 4 << ((conf->mode & GENMASK(29, 28)) >> 28));
> + } else {
> + printf("Standard read is applied.\n");
> + }
> +
> + tdf = (conf->mode & GENMASK(19, 16)) >> 16;
> + printf("TDF optimization %s\n",
> + (conf->mode & BIT(20)) ? "enabled" : "disabled");
> + printf("TDF cycles: %u (%u ns)\n", tdf, tdf * clk_period_ns);
> +
> + dbw = 8 << ((conf->mode & GENMASK(13, 12)) >> 12);
> + printf("Data Bus Width: %u-bit bus\n", dbw);
> + if (dbw > 8)
> + printf("Byte %s access type\n",
> + (conf->mode & BIT(8)) ? "write" : "select");
> +
> + printf("NWAIT Mode: %lu\n", (conf->mode & GENMASK(5, 4)) >> 4);
> + printf("Write operation controlled by %s signal\n",
> + (conf->mode & BIT(1)) ? "NWE" : "NCS");
> + printf("Read operation controlled by %s signal\n",
> + (conf->mode & BIT(0)) ? "NRD" : "NCS");
> +}
> +
> +static void atmel_hsmc_print_mode(struct atmel_smc_cs_conf *conf, u32 clk_period_ns)
> +{
> + u32 tdf;
> + u8 dbw;
> +
> + tdf = (conf->mode & GENMASK(19, 16)) >> 16;
> + printf("TDF optimization %s\n",
> + (conf->mode & BIT(20)) ? "enabled" : "disabled");
> + printf("TDF cycles: %u (%u ns)\n", tdf, tdf * clk_period_ns);
> +
> + dbw = 8 << ((conf->mode & BIT(12)) >> 12);
> + printf("Data Bus Width: %u-bit bus\n", dbw);
> + if (dbw > 8)
> + printf("Byte %s access type\n",
> + (conf->mode & BIT(8)) ? "write" : "select");
> +
> + printf("NWAIT Mode: %lu\n", (conf->mode & GENMASK(5, 4)) >> 4);
> + printf("Write operation controlled by %s signal\n",
> + (conf->mode & BIT(1)) ? "NWE" : "NCS");
> + printf("Read operation controlled by %s signal\n",
> + (conf->mode & BIT(0)) ? "NRD" : "NCS");
> +}
> +
> +static void atmel_hsmc_print_timings(struct atmel_smc_cs_conf *conf)
> +{
> + /* tbd */
> +}
> +
> +static void atmel_smc_print_info(struct atmel_nand *nand, int csline)
> +{
> + struct atmel_nand_controller *nc;
> + struct atmel_smc_cs_conf smcconf;
> + struct atmel_nand_cs *cs;
> + u32 mck_period_ns;
> +
> + nc = to_nand_controller(nand->controller);
> + cs = &nand->cs[csline];
> +
> + atmel_smc_cs_conf_init(&smcconf);
> + atmel_smc_cs_conf_get(nc->smc, cs->id, &smcconf);
> +
> + atmel_smc_cs_conf_print_raw(&smcconf, cs->id);
> +
> + mck_period_ns = NSEC_PER_SEC / clk_get_rate(nc->mck);
> +
> + atmel_smc_print_ncs_rd(&smcconf, mck_period_ns);
> + atmel_smc_print_nrd(&smcconf, mck_period_ns);
> + atmel_smc_print_ncs_wr(&smcconf, mck_period_ns);
> + atmel_smc_print_nwe(&smcconf, mck_period_ns);
> +
> + atmel_smc_print_mode(&smcconf, mck_period_ns);
> +}
> +
> +static void atmel_hsmc_print_info(struct atmel_nand *nand, int csline)
> +{
> + struct atmel_hsmc_nand_controller *hsmc_nc;
> + struct atmel_nand_controller *nc;
> + struct atmel_smc_cs_conf smcconf;
> + struct atmel_nand_cs *cs;
> + u32 mck_period_ns;
> +
> + nc = to_nand_controller(nand->controller);
> + hsmc_nc = to_hsmc_nand_controller(nand->controller);
> + cs = &nand->cs[csline];
> +
> + atmel_smc_cs_conf_init(&smcconf);
> + atmel_hsmc_cs_conf_get(nc->smc, hsmc_nc->hsmc_layout, cs->id, &smcconf);
> +
> + atmel_hsmc_cs_conf_print_raw(&smcconf, cs->id);
> +
> + mck_period_ns = NSEC_PER_SEC / clk_get_rate(nc->mck);
> +
> + atmel_smc_print_ncs_rd(&smcconf, mck_period_ns);
> + atmel_smc_print_nrd(&smcconf, mck_period_ns);
> + atmel_smc_print_ncs_wr(&smcconf, mck_period_ns);
> + atmel_smc_print_nwe(&smcconf, mck_period_ns);
> +
> + atmel_hsmc_print_mode(&smcconf, mck_period_ns);
> + atmel_hsmc_print_timings(&smcconf);
> +}
> +#endif
> +
> static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
> .probe = atmel_hsmc_nand_controller_probe,
> .remove = atmel_hsmc_nand_controller_remove,
> .ecc_init = atmel_hsmc_nand_ecc_init,
> .nand_init = atmel_hsmc_nand_init,
> .setup_data_interface = atmel_hsmc_nand_setup_data_interface,
> +#ifdef CONFIG_CMD_NAND_ATMEL_DEBUG
> + .print_info = atmel_hsmc_print_info,
> +#endif
> };
>
> static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
> @@ -2117,6 +2323,9 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
> .ecc_init = atmel_nand_ecc_init,
> .nand_init = atmel_smc_nand_init,
> .setup_data_interface = atmel_smc_nand_setup_data_interface,
> +#ifdef CONFIG_CMD_NAND_ATMEL_DEBUG
> + .print_info = atmel_smc_print_info,
> +#endif
> };
>
> static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
> @@ -2247,3 +2456,43 @@ void board_nand_init(void)
> printf("Failed to initialize NAND controller. (error %d)\n",
> ret);
> }
> +
> +#ifdef CONFIG_CMD_NAND_ATMEL_DEBUG
> +static int do_hsmc_decode(struct cmd_tbl *cmdtp, int flag,
> + int argc, char * const argv[])
> +{
> + struct atmel_nand_controller *nc;
> + struct atmel_nand *nand;
> + struct nand_chip *chip;
> + struct mtd_info *mtd;
> + int i, j;
> +
> + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
> + putc('\n');
> + mtd = get_nand_dev_by_index(i);
> + if (!mtd)
> + continue;
> +
> + chip = mtd_to_nand(mtd);
> + nand = to_atmel_nand(chip);
> + nc = to_nand_controller(nand->controller);
> + printf("mck clock rate: %lu\n", clk_get_rate(nc->mck));
> + if (!nc->caps->ops->print_info)
> + continue;
> +
> + for (j = 0; j < nand->numcs; j++) {
> + putc('\n');
> + nc->caps->ops->print_info(nand, j);
> + }
> + }
> +
> + return CMD_RET_SUCCESS;
> +}
> +
> +static char hsmc_help_text[] =
> + "decode - Decode SMC registers\n"
> + ;
> +
> +U_BOOT_CMD_WITH_SUBCMDS(hsmc, "Atmel Static Memory Controller (SMC) debugging", hsmc_help_text,
> + U_BOOT_SUBCMD_MKENT(decode, 1, 1, do_hsmc_decode));
> +#endif
More information about the U-Boot
mailing list