diff --git a/drivers/flash/spi_nor.c b/drivers/flash/spi_nor.c index 065795d492..b30dd171b9 100644 --- a/drivers/flash/spi_nor.c +++ b/drivers/flash/spi_nor.c @@ -48,36 +48,43 @@ LOG_MODULE_REGISTER(spi_nor, CONFIG_FLASH_LOG_LEVEL); #define SPI_NOR_MAX_ADDR_WIDTH 4 -#if DT_INST_NODE_HAS_PROP(0, t_enter_dpd) -#define T_DP_MS DIV_ROUND_UP(DT_INST_PROP(0, t_enter_dpd), NSEC_PER_MSEC) -#else /* T_ENTER_DPD */ -#define T_DP_MS 0 -#endif /* T_ENTER_DPD */ -#if DT_INST_NODE_HAS_PROP(0, t_exit_dpd) -#define T_RES1_MS DIV_ROUND_UP(DT_INST_PROP(0, t_exit_dpd), NSEC_PER_MSEC) -#endif /* T_EXIT_DPD */ -#if DT_INST_NODE_HAS_PROP(0, dpd_wakeup_sequence) -#define T_DPDD_MS DIV_ROUND_UP(DT_INST_PROP_BY_IDX(0, dpd_wakeup_sequence, 0), NSEC_PER_MSEC) -#define T_CRDP_MS DIV_ROUND_UP(DT_INST_PROP_BY_IDX(0, dpd_wakeup_sequence, 1), NSEC_PER_MSEC) -#define T_RDP_MS DIV_ROUND_UP(DT_INST_PROP_BY_IDX(0, dpd_wakeup_sequence, 2), NSEC_PER_MSEC) -#else /* DPD_WAKEUP_SEQUENCE */ -#define T_DPDD_MS 0 -#endif /* DPD_WAKEUP_SEQUENCE */ -#define _INST_HAS_WP_OR(inst) DT_INST_NODE_HAS_PROP(inst, wp_gpios) || -#define ANY_INST_HAS_WP_GPIOS DT_INST_FOREACH_STATUS_OKAY(_INST_HAS_WP_OR) 0 +#define ANY_INST_HAS_TRUE_(idx, bool_prop) \ + COND_CODE_1(DT_INST_PROP(idx, bool_prop), (1,), ()) -#define _INST_HAS_HOLD_OR(inst) DT_INST_NODE_HAS_PROP(inst, hold_gpios) || -#define ANY_INST_HAS_HOLD_GPIOS DT_INST_FOREACH_STATUS_OKAY(_INST_HAS_HOLD_OR) 0 +#define ANY_INST_HAS_TRUE(bool_prop) \ + COND_CODE_1(IS_EMPTY(DT_INST_FOREACH_STATUS_OKAY_VARGS(ANY_INST_HAS_TRUE_, bool_prop)), \ + (0), (1)) + +#define ANY_INST_HAS_PROP_(idx, prop_name) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, prop_name), (1,), ()) +#define ANY_INST_HAS_PROP(prop_name) \ + COND_CODE_1(IS_EMPTY(DT_INST_FOREACH_STATUS_OKAY_VARGS(ANY_INST_HAS_PROP_, prop_name)), \ + (0), (1)) + +#define ANY_INST_HAS_MXICY_MX25R_POWER_MODE ANY_INST_HAS_PROP(mxicy_mx25r_power_mode) +#define ANY_INST_HAS_DPD ANY_INST_HAS_TRUE(has_dpd) +#define ANY_INST_HAS_T_EXIT_DPD ANY_INST_HAS_PROP(t_exit_dpd) +#define ANY_INST_HAS_DPD_WAKEUP_SEQUENCE ANY_INST_HAS_PROP(dpd_wakeup_sequence) +#define ANY_INST_HAS_RESET_GPIOS ANY_INST_HAS_PROP(reset_gpios) +#define ANY_INST_HAS_WP_GPIOS ANY_INST_HAS_PROP(wp_gpios) +#define ANY_INST_HAS_HOLD_GPIOS ANY_INST_HAS_PROP(hold_gpios) #define DEV_CFG(_dev_) ((const struct spi_nor_config * const) (_dev_)->config) +/* MXICY Related defines*/ +/* MXICY Low-power/high perf mode is second bit in configuration register 2 */ +#define LH_SWITCH_BIT 9 + +#define JEDEC_MACRONIX_ID 0xc2 +#define JEDEC_MX25R_TYPE_ID 0x28 + /* Build-time data associated with the device. */ struct spi_nor_config { /* Devicetree SPI configuration */ struct spi_dt_spec spi; -#if DT_INST_NODE_HAS_PROP(0, reset_gpios) +#if ANY_INST_HAS_RESET_GPIOS const struct gpio_dt_spec reset; #endif @@ -119,12 +126,40 @@ struct spi_nor_config { #if ANY_INST_HAS_WP_GPIOS /* The write-protect GPIO (wp-gpios) */ - const struct gpio_dt_spec *wp; + const struct gpio_dt_spec wp; #endif + #if ANY_INST_HAS_HOLD_GPIOS /* The hold GPIO (hold-gpios) */ - const struct gpio_dt_spec *hold; + const struct gpio_dt_spec hold; #endif + +#if ANY_INST_HAS_DPD + uint16_t t_enter_dpd; /* in microseconds */ + uint16_t t_dpdd_ms; /* in microseconds */ +#if ANY_INST_HAS_T_EXIT_DPD + uint16_t t_exit_dpd; /* in microseconds */ +#endif +#endif + +#if ANY_INST_HAS_DPD_WAKEUP_SEQUENCE + uint16_t t_crdp_ms; /* in microseconds */ + uint16_t t_rdp_ms; /* in microseconds */ +#endif + +#if ANY_INST_HAS_MXICY_MX25R_POWER_MODE + bool mxicy_mx25r_power_mode; +#endif + + /* exist flags for dts opt-ins */ + bool dpd_exist:1; + bool dpd_wakeup_sequence_exist:1; + bool mxicy_mx25r_power_mode_exist:1; + bool enter_4byte_addr_exist:1; + bool reset_gpios_exist:1; + bool requires_ulbpr_exist:1; + bool wp_gpios_exist:1; + bool hold_gpios_exist:1; }; /** @@ -133,7 +168,7 @@ struct spi_nor_config { */ struct spi_nor_data { struct k_sem sem; -#if DT_INST_NODE_HAS_PROP(0, has_dpd) +#if ANY_INST_HAS_DPD /* Low 32-bits of uptime counter at which device last entered * deep power-down. */ @@ -249,10 +284,16 @@ static const struct flash_parameters flash_nor_parameters = { /* Capture the time at which the device entered deep power-down. */ static inline void record_entered_dpd(const struct device *const dev) { -#if DT_INST_NODE_HAS_PROP(0, has_dpd) - struct spi_nor_data *const driver_data = dev->data; +#if ANY_INST_HAS_DPD + const struct spi_nor_config *const driver_config = dev->config; - driver_data->ts_enter_dpd = k_uptime_get_32(); + if (driver_config->dpd_exist) { + struct spi_nor_data *const driver_data = dev->data; + + driver_data->ts_enter_dpd = k_uptime_get_32(); + } +#else + ARG_UNUSED(dev); #endif } @@ -261,31 +302,37 @@ static inline void record_entered_dpd(const struct device *const dev) */ static inline void delay_until_exit_dpd_ok(const struct device *const dev) { -#if DT_INST_NODE_HAS_PROP(0, has_dpd) - struct spi_nor_data *const driver_data = dev->data; - int32_t since = (int32_t)(k_uptime_get_32() - driver_data->ts_enter_dpd); +#if ANY_INST_HAS_DPD + const struct spi_nor_config *const driver_config = dev->config; - /* If the time is negative the 32-bit counter has wrapped, - * which is certainly long enough no further delay is - * required. Otherwise we have to check whether it's been - * long enough taking into account necessary delays for - * entering and exiting DPD. - */ - if (since >= 0) { - /* Subtract time required for DPD to be reached */ - since -= T_DP_MS; + if (driver_config->dpd_exist) { + struct spi_nor_data *const driver_data = dev->data; + int32_t since = (int32_t)(k_uptime_get_32() - driver_data->ts_enter_dpd); - /* Subtract time required in DPD before exit */ - since -= T_DPDD_MS; - - /* If the adjusted time is negative we have to wait - * until it reaches zero before we can proceed. + /* If the time is negative the 32-bit counter has wrapped, + * which is certainly long enough no further delay is + * required. Otherwise we have to check whether it's been + * long enough taking into account necessary delays for + * entering and exiting DPD. */ - if (since < 0) { - k_sleep(K_MSEC((uint32_t)-since)); + if (since >= 0) { + /* Subtract time required for DPD to be reached */ + since -= driver_config->t_enter_dpd; + + /* Subtract time required in DPD before exit */ + since -= driver_config->t_dpdd_ms; + + /* If the adjusted time is negative we have to wait + * until it reaches zero before we can proceed. + */ + if (since < 0) { + k_sleep(K_MSEC((uint32_t)-since)); + } } } -#endif /* DT_INST_NODE_HAS_PROP(0, has_dpd) */ +#else + ARG_UNUSED(dev); +#endif /* ANY_INST_HAS_DPD */ } /* Indicates that an access command includes bytes for the address. @@ -455,8 +502,10 @@ static int read_sfdp(const struct device *const dev, static int enter_dpd(const struct device *const dev) { int ret = 0; + const struct spi_nor_config *cfg = dev->config; - if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) { + ARG_UNUSED(cfg); + if (cfg->dpd_exist) { ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_DPD); if (ret == 0) { record_entered_dpd(dev); @@ -468,29 +517,34 @@ static int enter_dpd(const struct device *const dev) static int exit_dpd(const struct device *const dev) { int ret = 0; + const struct spi_nor_config *cfg = dev->config; - if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) { + if (cfg->dpd_exist) { delay_until_exit_dpd_ok(dev); -#if DT_INST_NODE_HAS_PROP(0, dpd_wakeup_sequence) - /* Assert CSn and wait for tCRDP. - * - * Unfortunately the SPI API doesn't allow us to - * control CSn so fake it by writing a known-supported - * single-byte command, hoping that'll hold the assert - * long enough. This is highly likely, since the - * duration is usually less than two SPI clock cycles. - */ - ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_RDID); +#if ANY_INST_HAS_DPD_WAKEUP_SEQUENCE + if (cfg->dpd_wakeup_sequence_exist) { + /* Assert CSn and wait for tCRDP. + * + * Unfortunately the SPI API doesn't allow us to + * control CSn so fake it by writing a known-supported + * single-byte command, hoping that'll hold the assert + * long enough. This is highly likely, since the + * duration is usually less than two SPI clock cycles. + */ + ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_RDID); - /* Deassert CSn and wait for tRDP */ - k_sleep(K_MSEC(T_RDP_MS)); -#else /* DPD_WAKEUP_SEQUENCE */ - ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_RDPD); + /* Deassert CSn and wait for tRDP */ + k_sleep(K_MSEC(cfg->t_rdp_ms)); + } else { + ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_RDPD); - if (ret == 0) { -#if DT_INST_NODE_HAS_PROP(0, t_exit_dpd) - k_sleep(K_MSEC(T_RES1_MS)); +#if ANY_INST_HAS_T_EXIT_DPD + if (ret == 0) { + if (cfg->dpd_exist) { + k_sleep(K_MSEC(cfg->t_exit_dpd)); + } + } #endif /* T_EXIT_DPD */ } #endif /* DPD_WAKEUP_SEQUENCE */ @@ -581,7 +635,7 @@ static int spi_nor_wrsr(const struct device *dev, return ret; } -#if DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode) +#if ANY_INST_HAS_MXICY_MX25R_POWER_MODE /** * @brief Read the configuration register. @@ -595,12 +649,15 @@ static int spi_nor_wrsr(const struct device *dev, */ static int mxicy_rdcr(const struct device *dev) { - uint16_t cr; - enum { CMD_RDCR = 0x15 }; - int ret = spi_nor_cmd_read(dev, CMD_RDCR, &cr, sizeof(cr)); + const struct spi_nor_config *cfg = dev->config; + uint16_t cr = -ENOSYS; - if (ret < 0) { - return ret; + if (cfg->mxicy_mx25r_power_mode_exist) { + int ret = spi_nor_cmd_read(dev, CMD_RDCR, &cr, sizeof(cr)); + + if (ret < 0) { + return ret; + } } return cr; @@ -620,30 +677,35 @@ static int mxicy_rdcr(const struct device *dev) static int mxicy_wrcr(const struct device *dev, uint16_t cr) { + const struct spi_nor_config *cfg = dev->config; + int ret = -ENOSYS; /* The configuration register bytes on the Macronix MX25R devices are * written using the Write Status Register command where the configuration * register bytes are written as two extra bytes after the status register. * First read out the current status register to preserve the value. */ - int sr = spi_nor_rdsr(dev); - if (sr < 0) { - LOG_ERR("Read status register failed: %d", sr); - return sr; - } + if (cfg->mxicy_mx25r_power_mode_exist) { + int sr = spi_nor_rdsr(dev); - int ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN); + if (sr < 0) { + LOG_ERR("Read status register failed: %d", sr); + return sr; + } - if (ret == 0) { - uint8_t data[] = { - sr, - cr & 0xFF, /* Configuration register 1 */ - cr >> 8 /* Configuration register 2 */ - }; + ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN); - ret = spi_nor_access(dev, SPI_NOR_CMD_WRSR, NOR_ACCESS_WRITE, 0, data, - sizeof(data)); - spi_nor_wait_until_ready(dev, WAIT_READY_REGISTER); + if (ret == 0) { + uint8_t data[] = { + sr, + cr & 0xFF, /* Configuration register 1 */ + cr >> 8 /* Configuration register 2 */ + }; + + ret = spi_nor_access(dev, SPI_NOR_CMD_WRSR, NOR_ACCESS_WRITE, 0, + data, sizeof(data)); + spi_nor_wait_until_ready(dev, WAIT_READY_REGISTER); + } } return ret; @@ -651,55 +713,57 @@ static int mxicy_wrcr(const struct device *dev, static int mxicy_configure(const struct device *dev, const uint8_t *jedec_id) { - /* Low-power/high perf mode is second bit in configuration register 2 */ - enum { LH_SWITCH_BIT = 9 }; - const uint8_t JEDEC_MACRONIX_ID = 0xc2; - const uint8_t JEDEC_MX25R_TYPE_ID = 0x28; - int current_cr, new_cr, ret; - /* lh_switch enum index: - * 0: Ultra low power - * 1: High performance mode - */ - const bool use_high_perf = DT_INST_ENUM_IDX(0, mxicy_mx25r_power_mode); + const struct spi_nor_config *cfg = dev->config; + int ret = -ENOSYS; - /* Only supported on Macronix MX25R Ultra Low Power series. */ - if (jedec_id[0] != JEDEC_MACRONIX_ID || jedec_id[1] != JEDEC_MX25R_TYPE_ID) { - LOG_WRN("L/H switch not supported for device id: %02x %02x %02x", jedec_id[0], - jedec_id[1], jedec_id[2]); - /* Do not return an error here because the flash still functions */ - return 0; - } + if (cfg->mxicy_mx25r_power_mode_exist) { + /* Low-power/high perf mode is second bit in configuration register 2 */ + int current_cr, new_cr; + /* lh_switch enum index: + * 0: Ultra low power + * 1: High performance mode + */ + const bool use_high_perf = cfg->mxicy_mx25r_power_mode; - acquire_device(dev); + /* Only supported on Macronix MX25R Ultra Low Power series. */ + if (jedec_id[0] != JEDEC_MACRONIX_ID || jedec_id[1] != JEDEC_MX25R_TYPE_ID) { + LOG_WRN("L/H switch not supported for device id: %02x %02x %02x", + jedec_id[0], jedec_id[1], jedec_id[2]); + /* Do not return an error here because the flash still functions */ + return 0; + } - /* Read current configuration register */ + acquire_device(dev); + + /* Read current configuration register */ + + ret = mxicy_rdcr(dev); + if (ret < 0) { + release_device(dev); + return ret; + } + current_cr = ret; + + LOG_DBG("Use high performance mode? %d", use_high_perf); + new_cr = current_cr; + WRITE_BIT(new_cr, LH_SWITCH_BIT, use_high_perf); + if (new_cr != current_cr) { + ret = mxicy_wrcr(dev, new_cr); + } else { + ret = 0; + } + + if (ret < 0) { + LOG_ERR("Enable high performace mode failed: %d", ret); + } - ret = mxicy_rdcr(dev); - if (ret < 0) { release_device(dev); - return ret; } - current_cr = ret; - - LOG_DBG("Use high performance mode? %d", use_high_perf); - new_cr = current_cr; - WRITE_BIT(new_cr, LH_SWITCH_BIT, use_high_perf); - if (new_cr != current_cr) { - ret = mxicy_wrcr(dev, new_cr); - } else { - ret = 0; - } - - if (ret < 0) { - LOG_ERR("Enable high performace mode failed: %d", ret); - } - - release_device(dev); return ret; } -#endif /* DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode) */ +#endif /* ANY_INST_HAS_MXICY_MX25R_POWER_MODE */ static int spi_nor_read(const struct device *dev, off_t addr, void *dest, size_t size) @@ -892,25 +956,27 @@ static int spi_nor_write_protection_set(const struct device *dev, bool write_protect) { int ret; + const struct spi_nor_config *cfg = dev->config; #if ANY_INST_HAS_WP_GPIOS - if (DEV_CFG(dev)->wp && write_protect == false) { - gpio_pin_set_dt(DEV_CFG(dev)->wp, 0); + if (DEV_CFG(dev)->wp_gpios_exist && write_protect == false) { + gpio_pin_set_dt(&(DEV_CFG(dev)->wp), 0); } #endif + ARG_UNUSED(cfg); ret = spi_nor_cmd_write(dev, (write_protect) ? SPI_NOR_CMD_WRDI : SPI_NOR_CMD_WREN); - if (IS_ENABLED(DT_INST_PROP(0, requires_ulbpr)) + if (cfg->requires_ulbpr_exist && (ret == 0) && !write_protect) { ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_ULBPR); } #if ANY_INST_HAS_WP_GPIOS - if (DEV_CFG(dev)->wp && write_protect == true) { - gpio_pin_set_dt(DEV_CFG(dev)->wp, 1); + if (DEV_CFG(dev)->wp_gpios_exist && write_protect == true) { + gpio_pin_set_dt(&(DEV_CFG(dev)->wp), 1); } #endif @@ -970,8 +1036,10 @@ static int spi_nor_read_jedec_id(const struct device *dev, static int spi_nor_set_address_mode(const struct device *dev, uint8_t enter_4byte_addr) { - int ret = 0; + const struct spi_nor_config *cfg = dev->config; + int ret = -ENOSYS; + if (cfg->enter_4byte_addr_exist) { /* Do nothing if not provided (either no bits or all bits * set). */ @@ -1006,6 +1074,7 @@ static int spi_nor_set_address_mode(const struct device *dev, } release_device(dev); + } return ret; } @@ -1226,18 +1295,21 @@ static int spi_nor_configure(const struct device *dev) return -ENODEV; } -#if DT_INST_NODE_HAS_PROP(0, reset_gpios) - if (!gpio_is_ready_dt(&cfg->reset)) { - LOG_ERR("Reset pin not ready"); - return -ENODEV; - } - if (gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_ACTIVE)) { - LOG_ERR("Couldn't configure reset pin"); - return -ENODEV; - } - rc = gpio_pin_set_dt(&cfg->reset, 0); - if (rc) { - return rc; +#if ANY_INST_HAS_RESET_GPIOS + + if (cfg->reset_gpios_exist) { + if (!gpio_is_ready_dt(&cfg->reset)) { + LOG_ERR("Reset pin not ready"); + return -ENODEV; + } + if (gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_ACTIVE)) { + LOG_ERR("Couldn't configure reset pin"); + return -ENODEV; + } + rc = gpio_pin_set_dt(&cfg->reset, 0); + if (rc) { + return rc; + } } #endif @@ -1339,10 +1411,12 @@ static int spi_nor_configure(const struct device *dev) #endif /* CONFIG_FLASH_PAGE_LAYOUT */ #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */ -#if DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode) +#if ANY_INST_HAS_MXICY_MX25R_POWER_MODE + if (cfg->mxicy_mx25r_power_mode_exist) { /* Do not fail init if setting configuration register fails */ - (void) mxicy_configure(dev, jedec_id); -#endif /* DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode) */ + (void)mxicy_configure(dev, jedec_id); + } +#endif /* ANY_INST_HAS_MXICY_MX25R_POWER_MODE */ if (IS_ENABLED(CONFIG_SPI_NOR_IDLE_IN_DPD) && (enter_dpd(dev) != 0)) { @@ -1415,24 +1489,24 @@ static int spi_nor_init(const struct device *dev) } #if ANY_INST_HAS_WP_GPIOS - if (DEV_CFG(dev)->wp) { - if (!device_is_ready(DEV_CFG(dev)->wp->port)) { + if (DEV_CFG(dev)->wp_gpios_exist) { + if (!device_is_ready(DEV_CFG(dev)->wp.port)) { LOG_ERR("Write-protect pin not ready"); return -ENODEV; } - if (gpio_pin_configure_dt(DEV_CFG(dev)->wp, GPIO_OUTPUT_ACTIVE)) { + if (gpio_pin_configure_dt(&(DEV_CFG(dev)->wp), GPIO_OUTPUT_ACTIVE)) { LOG_ERR("Write-protect pin failed to set active"); return -ENODEV; } } #endif /* ANY_INST_HAS_WP_GPIOS */ #if ANY_INST_HAS_HOLD_GPIOS - if (DEV_CFG(dev)->hold) { - if (!device_is_ready(DEV_CFG(dev)->hold->port)) { + if (DEV_CFG(dev)->hold_gpios_exist) { + if (!device_is_ready(DEV_CFG(dev)->hold.port)) { LOG_ERR("Hold pin not ready"); return -ENODEV; } - if (gpio_pin_configure_dt(DEV_CFG(dev)->hold, GPIO_OUTPUT_INACTIVE)) { + if (gpio_pin_configure_dt(&(DEV_CFG(dev)->hold), GPIO_OUTPUT_INACTIVE)) { LOG_ERR("Hold pin failed to set inactive"); return -ENODEV; } @@ -1489,117 +1563,144 @@ static const struct flash_driver_api spi_nor_api = { #endif }; -#ifndef CONFIG_SPI_NOR_SFDP_RUNTIME -/* We need to know the size and ID of the configuration data we're - * using so we can disable the device we see at runtime if it isn't - * compatible with what we're taking from devicetree or minimal. - */ -BUILD_ASSERT(DT_INST_NODE_HAS_PROP(0, jedec_id), - "jedec,spi-nor jedec-id required for non-runtime SFDP"); - -#if defined(CONFIG_FLASH_PAGE_LAYOUT) - -/* For devicetree or minimal page layout we need to know the size of - * the device. We can't extract it from the raw BFP data, so require - * it to be present in devicetree. - */ -BUILD_ASSERT(DT_INST_NODE_HAS_PROP(0, size), - "jedec,spi-nor size required for non-runtime SFDP page layout"); - -/* instance 0 size in bytes */ -#define INST_0_BYTES (DT_INST_PROP(0, size) / 8) - -BUILD_ASSERT(SPI_NOR_IS_SECTOR_ALIGNED(CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE), - "SPI_NOR_FLASH_LAYOUT_PAGE_SIZE must be multiple of 4096"); - -/* instance 0 page count */ -#define LAYOUT_PAGES_COUNT (INST_0_BYTES / CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE) - -BUILD_ASSERT((CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE * LAYOUT_PAGES_COUNT) - == INST_0_BYTES, +#define PAGE_LAYOUT_GEN(idx) \ + BUILD_ASSERT(DT_INST_NODE_HAS_PROP(idx, size), \ + "jedec,spi-nor size required for non-runtime SFDP page layout"); \ + enum { \ + INST_##idx##_BYTES = (DT_INST_PROP(idx, size) / 8) \ + }; \ + BUILD_ASSERT(SPI_NOR_IS_SECTOR_ALIGNED(CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE), \ + "SPI_NOR_FLASH_LAYOUT_PAGE_SIZE must be multiple of 4096"); \ + enum { \ + LAYOUT_PAGES_##idx##_COUNT = \ + (INST_##idx##_BYTES / CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE) \ + }; \ + BUILD_ASSERT((CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE * LAYOUT_PAGES_##idx##_COUNT) == \ + INST_##idx##_BYTES, \ "SPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size"); -#endif /* CONFIG_FLASH_PAGE_LAYOUT */ +#define SFDP_BFP_ATTR_GEN(idx) \ + BUILD_ASSERT(DT_INST_NODE_HAS_PROP(idx, sfdp_bfp), \ + "jedec,spi-nor sfdp-bfp required for devicetree SFDP"); \ + static const __aligned(4) uint8_t bfp_##idx##_data[] = DT_INST_PROP(idx, sfdp_bfp); -#ifdef CONFIG_SPI_NOR_SFDP_DEVICETREE -BUILD_ASSERT(DT_INST_NODE_HAS_PROP(0, sfdp_bfp), - "jedec,spi-nor sfdp-bfp required for devicetree SFDP"); +#define INST_ATTR_GEN(idx) \ + BUILD_ASSERT(DT_INST_NODE_HAS_PROP(idx, jedec_id), \ + "jedec,spi-nor jedec-id required for non-runtime SFDP"); \ + IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, (PAGE_LAYOUT_GEN(idx))) \ + IF_ENABLED(CONFIG_SPI_NOR_SFDP_DEVICETREE, (SFDP_BFP_ATTR_GEN(idx))) -static const __aligned(4) uint8_t bfp_data_0[] = DT_INST_PROP(0, sfdp_bfp); -#endif /* CONFIG_SPI_NOR_SFDP_DEVICETREE */ +#define ATTRIBUTES_DEFINE(idx) COND_CODE_1(CONFIG_SPI_NOR_SFDP_RUNTIME, EMPTY(), \ + (INST_ATTR_GEN(idx))) -#endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */ +#define DEFINE_PAGE_LAYOUT(idx) \ + IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, \ + (.layout = { \ + .pages_count = LAYOUT_PAGES_##idx##_COUNT, \ + .pages_size = CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE, \ + },)) -#if DT_INST_NODE_HAS_PROP(0, has_lock) -/* Currently we only know of devices where the BP bits are present in - * the first byte of the status register. Complain if that changes. - */ -BUILD_ASSERT(DT_INST_PROP(0, has_lock) == (DT_INST_PROP(0, has_lock) & 0xFF), - "Need support for lock clear beyond SR1"); -#endif +#define INST_HAS_LOCK(idx) DT_INST_NODE_HAS_PROP(idx, has_lock) #define INST_HAS_WP_GPIO(idx) DT_INST_NODE_HAS_PROP(idx, wp_gpios) -#define INST_WP_GPIO_SPEC(idx) \ - IF_ENABLED(INST_HAS_WP_GPIO(idx), (static const struct gpio_dt_spec wp_##idx = \ - GPIO_DT_SPEC_INST_GET(idx, wp_gpios);)) - #define INST_HAS_HOLD_GPIO(idx) DT_INST_NODE_HAS_PROP(idx, hold_gpios) -#define INST_HOLD_GPIO_SPEC(idx) \ - IF_ENABLED(INST_HAS_HOLD_GPIO(idx), (static const struct gpio_dt_spec hold_##idx = \ - GPIO_DT_SPEC_INST_GET(idx, hold_gpios);)) +#define LOCK_DEFINE(idx) \ + IF_ENABLED(INST_HAS_LOCK(idx), (BUILD_ASSERT(DT_INST_PROP(idx, has_lock) == \ + (DT_INST_PROP(idx, has_lock) & 0xFF), \ + "Need support for lock clear beyond SR1");)) -INST_WP_GPIO_SPEC(0) -INST_HOLD_GPIO_SPEC(0) +#define INST_HAS_ENTER_4BYTE_ADDR(idx) DT_INST_NODE_HAS_PROP(idx, enter_4byte_addr) -static const struct spi_nor_config spi_nor_config_0 = { - .spi = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8), - CONFIG_SPI_NOR_CS_WAIT_DELAY), -#if DT_INST_NODE_HAS_PROP(0, reset_gpios) - .reset = GPIO_DT_SPEC_INST_GET(0, reset_gpios), +#define CONFIGURE_4BYTE_ADDR(idx) \ + IF_ENABLED(INST_HAS_ENTER_4BYTE_ADDR(idx), \ + (.enter_4byte_addr = DT_INST_PROP(idx, enter_4byte_addr),)) + +#define INIT_T_ENTER_DPD(idx) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, t_enter_dpd), \ + (.t_enter_dpd = \ + DIV_ROUND_UP(DT_INST_PROP(idx, t_enter_dpd), NSEC_PER_MSEC)),\ + (.t_enter_dpd = 0)) + +#if ANY_INST_HAS_T_EXIT_DPD +#define INIT_T_EXIT_DPD(idx) \ + COND_CODE_1( \ + DT_INST_NODE_HAS_PROP(idx, t_exit_dpd), \ + (.t_exit_dpd = DIV_ROUND_UP(DT_INST_PROP(idx, t_exit_dpd), NSEC_PER_MSEC)),\ + (.t_exit_dpd = 0)) #endif -#if !defined(CONFIG_SPI_NOR_SFDP_RUNTIME) +#define INIT_WP_GPIOS(idx) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, wp_gpios), \ + (.wp = GPIO_DT_SPEC_INST_GET(idx, wp_gpios)), \ + (.wp = {0})) -#if defined(CONFIG_FLASH_PAGE_LAYOUT) - .layout = { - .pages_count = LAYOUT_PAGES_COUNT, - .pages_size = CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE, - }, -#undef LAYOUT_PAGES_COUNT -#endif /* CONFIG_FLASH_PAGE_LAYOUT */ +#define INIT_HOLD_GPIOS(idx) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, hold_gpios), \ + (.hold = GPIO_DT_SPEC_INST_GET(idx, hold_gpios)), \ + (.hold = {0},)) - .flash_size = DT_INST_PROP(0, size) / 8, - .jedec_id = DT_INST_PROP(0, jedec_id), +#define INIT_WAKEUP_SEQ_PARAMS(idx) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, dpd_wakeup_sequence), \ + (.t_dpdd_ms = DIV_ROUND_UP( \ + DT_INST_PROP_BY_IDX(idx, dpd_wakeup_sequence, 0), NSEC_PER_MSEC),\ + .t_crdp_ms = DIV_ROUND_UP( \ + DT_INST_PROP_BY_IDX(idx, dpd_wakeup_sequence, 1), NSEC_PER_MSEC),\ + .t_rdp_ms = DIV_ROUND_UP( \ + DT_INST_PROP_BY_IDX(idx, dpd_wakeup_sequence, 2), NSEC_PER_MSEC)),\ + (.t_dpdd_ms = 0, .t_crdp_ms = 0, .t_rdp_ms = 0)) -#if DT_INST_NODE_HAS_PROP(0, has_lock) - .has_lock = DT_INST_PROP(0, has_lock), -#endif -#if defined(CONFIG_SPI_NOR_SFDP_MINIMAL) \ - && DT_INST_NODE_HAS_PROP(0, enter_4byte_addr) - .enter_4byte_addr = DT_INST_PROP(0, enter_4byte_addr), -#endif -#ifdef CONFIG_SPI_NOR_SFDP_DEVICETREE - .bfp_len = sizeof(bfp_data_0) / 4, - .bfp = (const struct jesd216_bfp *)bfp_data_0, -#endif /* CONFIG_SPI_NOR_SFDP_DEVICETREE */ +#define INIT_MXICY_MX25R_POWER_MODE(idx) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, mxicy_mx25r_power_mode), \ + (.mxicy_mx25r_power_mode = DT_INST_ENUM_IDX(idx, mxicy_mx25r_power_mode)),\ + (.mxicy_mx25r_power_mode = 0)) -#endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */ +#define INIT_RESET_GPIOS(idx) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, reset_gpios), \ + (.reset = GPIO_DT_SPEC_INST_GET(idx, reset_gpios)), \ + (.reset = {0})) -#if DT_INST_NODE_HAS_PROP(0, wp_gpios) - .wp = &wp_0, -#endif +#define INST_CONFIG_STRUCT_GEN(idx) \ + DEFINE_PAGE_LAYOUT(idx) \ + .flash_size = DT_INST_PROP(idx, size) / 8, \ + .jedec_id = DT_INST_PROP(idx, jedec_id), \ + .dpd_exist = DT_INST_PROP(idx, has_dpd), \ + .dpd_wakeup_sequence_exist = DT_INST_NODE_HAS_PROP(idx, dpd_wakeup_sequence), \ + .mxicy_mx25r_power_mode_exist = DT_INST_NODE_HAS_PROP(idx, mxicy_mx25r_power_mode), \ + .enter_4byte_addr_exist = DT_INST_NODE_HAS_PROP(idx, enter_4byte_addr), \ + .reset_gpios_exist = DT_INST_NODE_HAS_PROP(idx, reset_gpios), \ + .requires_ulbpr_exist = DT_INST_PROP(idx, requires_ulbpr), \ + .wp_gpios_exist = DT_INST_NODE_HAS_PROP(idx, wp_gpios), \ + .hold_gpios_exist = DT_INST_NODE_HAS_PROP(idx, hold_gpios), \ + IF_ENABLED(INST_HAS_LOCK(idx), (.has_lock = DT_INST_PROP(idx, has_lock),)) \ + IF_ENABLED(CONFIG_SPI_NOR_SFDP_MINIMAL, (CONFIGURE_4BYTE_ADDR(idx))) \ + IF_ENABLED(CONFIG_SPI_NOR_SFDP_DEVICETREE, \ + (.bfp_len = sizeof(bfp_##idx##_data) / 4, \ + .bfp = (const struct jesd216_bfp *)bfp_##idx##_data,)) \ + IF_ENABLED(ANY_INST_HAS_DPD, (INIT_T_ENTER_DPD(idx),)) \ + IF_ENABLED(UTIL_AND(ANY_INST_HAS_DPD, ANY_INST_HAS_T_EXIT_DPD), (INIT_T_EXIT_DPD(idx),))\ + IF_ENABLED(ANY_INST_HAS_DPD_WAKEUP_SEQUENCE, (INIT_WAKEUP_SEQ_PARAMS(idx),)) \ + IF_ENABLED(ANY_INST_HAS_MXICY_MX25R_POWER_MODE, (INIT_MXICY_MX25R_POWER_MODE(idx),)) \ + IF_ENABLED(ANY_INST_HAS_RESET_GPIOS, (INIT_RESET_GPIOS(idx),)) \ + IF_ENABLED(ANY_INST_HAS_WP_GPIOS, (INIT_WP_GPIOS(idx),)) -#if DT_INST_NODE_HAS_PROP(0, hold_gpios) - .hold = &hold_0, -#endif -}; +#define GENERATE_CONFIG_STRUCT(idx) \ + static const struct spi_nor_config spi_nor_##idx##_config = { \ + .spi = SPI_DT_SPEC_INST_GET(idx, SPI_WORD_SET(8), CONFIG_SPI_NOR_CS_WAIT_DELAY),\ + COND_CODE_1(CONFIG_SPI_NOR_SFDP_RUNTIME, EMPTY(), (INST_CONFIG_STRUCT_GEN(idx)))}; -static struct spi_nor_data spi_nor_data_0; +#define ASSIGN_PM(idx) \ + PM_DEVICE_DT_INST_DEFINE(idx, spi_nor_pm_control); -PM_DEVICE_DT_INST_DEFINE(0, spi_nor_pm_control); -DEVICE_DT_INST_DEFINE(0, &spi_nor_init, PM_DEVICE_DT_INST_GET(0), - &spi_nor_data_0, &spi_nor_config_0, - POST_KERNEL, CONFIG_SPI_NOR_INIT_PRIORITY, - &spi_nor_api); +#define SPI_NOR_INST(idx) \ + ASSIGN_PM(idx) \ + ATTRIBUTES_DEFINE(idx) \ + LOCK_DEFINE(idx) \ + GENERATE_CONFIG_STRUCT(idx) \ + static struct spi_nor_data spi_nor_##idx##_data; \ + DEVICE_DT_INST_DEFINE(idx, &spi_nor_init, PM_DEVICE_DT_INST_GET(idx), \ + &spi_nor_##idx##_data, &spi_nor_##idx##_config, \ + POST_KERNEL, CONFIG_SPI_NOR_INIT_PRIORITY, &spi_nor_api); + +DT_INST_FOREACH_STATUS_OKAY(SPI_NOR_INST) diff --git a/drivers/flash/spi_nor.h b/drivers/flash/spi_nor.h index 8d54cc6150..5f38c98289 100644 --- a/drivers/flash/spi_nor.h +++ b/drivers/flash/spi_nor.h @@ -113,4 +113,6 @@ #define SPI_NOR_IS_32K_ALIGNED(_ofs) SPI_NOR_IS_ALIGNED(_ofs, 15) #define SPI_NOR_IS_64K_ALIGNED(_ofs) SPI_NOR_IS_ALIGNED(_ofs, 16) +#define CMD_RDCR 0x15 /* Read the configuration register. */ + #endif /*__SPI_NOR_H__*/