[PATCH 4/4] mtd: nand: raw: atmel: Introduce optional debug commands
Alexander Dahl
ada at thorsis.com
Thu Mar 7 10:10:14 CET 2024
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
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
--
2.39.2
More information about the U-Boot
mailing list