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 <a...@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