1818
1919LOG_MODULE_REGISTER (spi_nor , CONFIG_FLASH_LOG_LEVEL );
2020
21+ /* Device Power Management Notes
22+ *
23+ * These flash devices have several modes during operation:
24+ * * When CSn is asserted (during a SPI operation) the device is
25+ * active.
26+ * * When CSn is deasserted the device enters a standby mode.
27+ * * Some devices support a Deep Power-Down mode which reduces current
28+ * to as little as 0.1% of standby.
29+ *
30+ * The power reduction from DPD is sufficent to warrant allowing its
31+ * use even in cases where Zephyr's device power management is not
32+ * available. This is selected through the SPI_NOR_IDLE_IN_DPD
33+ * Kconfig option.
34+ *
35+ * When mapped to the Zephyr Device Power Management states:
36+ * * DEVICE_PM_ACTIVE_STATE covers both active and standby modes;
37+ * * DEVICE_PM_LOW_POWER_STATE, DEVICE_PM_SUSPEND_STATE, and
38+ * DEVICE_PM_OFF_STATE all correspond to deep-power-down mode.
39+ */
40+
2141#define SPI_NOR_MAX_ADDR_WIDTH 4
2242
43+ #ifndef NSEC_PER_MSEC
44+ #define NSEC_PER_MSEC (NSEC_PER_USEC * USEC_PER_MSEC)
45+ #endif
46+
47+ #ifdef DT_INST_0_JEDEC_SPI_NOR_T_ENTER_DPD
48+ #define T_DP_MS ceiling_fraction(DT_INST_0_JEDEC_SPI_NOR_T_ENTER_DPD, NSEC_PER_MSEC)
49+ #endif /* T_ENTER_DPD */
50+ #ifdef DT_INST_0_JEDEC_SPI_NOR_T_EXIT_DPD
51+ #define T_RES1_MS ceiling_fraction(DT_INST_0_JEDEC_SPI_NOR_T_EXIT_DPD, NSEC_PER_MSEC)
52+ #endif /* T_EXIT_DPD */
53+ #ifdef DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE
54+ #define T_DPDD_MS ceiling_fraction(DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE_0, NSEC_PER_MSEC)
55+ #define T_CRDP_MS ceiling_fraction(DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE_1, NSEC_PER_MSEC)
56+ #define T_RDP_MS ceiling_fraction(DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE_2, NSEC_PER_MSEC)
57+ #endif /* DPD_WAKEUP_SEQUENCE */
58+
2359/**
2460 * struct spi_nor_data - Structure for defining the SPI NOR access
2561 * @spi: The SPI device
@@ -33,9 +69,59 @@ struct spi_nor_data {
3369#ifdef DT_INST_0_JEDEC_SPI_NOR_CS_GPIOS_CONTROLLER
3470 struct spi_cs_control cs_ctrl ;
3571#endif /* DT_INST_0_JEDEC_SPI_NOR_CS_GPIOS_CONTROLLER */
72+ #ifdef DT_INST_0_JEDEC_SPI_NOR_HAS_DPD
73+ /* Low 32-bits of uptime counter at which device last entered
74+ * deep power-down.
75+ */
76+ u32_t ts_enter_dpd ;
77+ #endif
3678 struct k_sem sem ;
3779};
3880
81+ /* Capture the time at which the device entered deep power-down. */
82+ static inline void record_entered_dpd (const struct device * const dev )
83+ {
84+ #ifdef DT_INST_0_JEDEC_SPI_NOR_HAS_DPD
85+ struct spi_nor_data * const driver_data = dev -> driver_data ;
86+
87+ driver_data -> ts_enter_dpd = k_uptime_get_32 ();
88+ #endif
89+ }
90+
91+ /* Check the current time against the time DPD was entered and delay
92+ * until it's ok to initiate the DPD exit process.
93+ */
94+ static inline void delay_until_exit_dpd_ok (const struct device * const dev )
95+ {
96+ #ifdef DT_INST_0_JEDEC_SPI_NOR_HAS_DPD
97+ struct spi_nor_data * const driver_data = dev -> driver_data ;
98+ s32_t since = (s32_t )(k_uptime_get_32 () - driver_data -> ts_enter_dpd );
99+
100+ /* If the time is negative the 32-bit counter has wrapped,
101+ * which is certainly long enough no further delay is
102+ * required. Otherwise we have to check whether it's been
103+ * long enough.
104+ */
105+ if (since >= 0 ) {
106+ #ifdef DT_INST_0_JEDEC_SPI_NOR_T_ENTER_DPD
107+ /* Subtract time required for DPD to be reached */
108+ since -= T_DP_MS ;
109+ #endif /* T_ENTER_DPD */
110+ #ifdef DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE
111+ /* Subtract time required in DPD before exit */
112+ since -= T_DPDD_MS ;
113+ #endif /* DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE */
114+
115+ /* If the adjusted time is negative we have to wait
116+ * until it reaches zero before we can proceed.
117+ */
118+ if (since < 0 ) {
119+ k_sleep (K_MSEC ((u32_t )- since ));
120+ }
121+ }
122+ #endif /* DT_INST_0_JEDEC_SPI_NOR_HAS_DPD */
123+ }
124+
39125/*
40126 * @brief Send an SPI command
41127 *
@@ -99,9 +185,56 @@ static int spi_nor_access(const struct device *const dev,
99185#define spi_nor_cmd_addr_write (dev , opcode , addr , src , length ) \
100186 spi_nor_access(dev, opcode, true, addr, (void *)src, length, true)
101187
188+ static int enter_dpd (const struct device * const dev )
189+ {
190+ int ret = 0 ;
191+
192+ if (IS_ENABLED (DT_INST_0_JEDEC_SPI_NOR_HAS_DPD )) {
193+ ret = spi_nor_cmd_write (dev , SPI_NOR_CMD_DPD );
194+ if (ret == 0 ) {
195+ record_entered_dpd (dev );
196+ }
197+ }
198+ return ret ;
199+ }
200+
201+ static int exit_dpd (const struct device * const dev )
202+ {
203+ int ret = 0 ;
204+
205+ if (IS_ENABLED (DT_INST_0_JEDEC_SPI_NOR_HAS_DPD )) {
206+ delay_until_exit_dpd_ok (dev );
207+
208+ #ifdef DT_INST_0_JEDEC_SPI_NOR_DPD_WAKEUP_SEQUENCE
209+ /* Assert CSn and wait for tCRDP.
210+ *
211+ * Unfortunately the SPI API doesn't allow us to
212+ * control CSn so fake it by writing a known-supported
213+ * single-byte command, hoping that'll hold the assert
214+ * long enough. This is highly likely, since the
215+ * duration is usually less than two SPI clock cycles.
216+ */
217+ ret = spi_nor_cmd_write (dev , SPI_NOR_CMD_RDID );
218+
219+ /* Deassert CSn and wait for tRDP */
220+ k_sleep (K_MSEC (T_RDP_MS ));
221+ #else /* DPD_WAKEUP_SEQUENCE */
222+ ret = spi_nor_cmd_write (dev , SPI_NOR_CMD_RDPD );
223+
224+ if (ret == 0 ) {
225+ #ifdef DT_INST_0_JEDEC_SPI_NOR_T_EXIT_DPD
226+ k_sleep (K_MSEC (T_RES1_MS ));
227+ #endif /* T_EXIT_DPD */
228+ }
229+ #endif /* DPD_WAKEUP_SEQUENCE */
230+ }
231+ return ret ;
232+ }
233+
102234/* Everything necessary to acquire owning access to the device.
103235 *
104- * For now this means taking the lock.
236+ * This means taking the lock and, if necessary, waking the device
237+ * from deep power-down mode.
105238 */
106239static void acquire_device (struct device * dev )
107240{
@@ -110,14 +243,23 @@ static void acquire_device(struct device *dev)
110243
111244 k_sem_take (& driver_data -> sem , K_FOREVER );
112245 }
246+
247+ if (IS_ENABLED (CONFIG_SPI_NOR_IDLE_IN_DPD )) {
248+ exit_dpd (dev );
249+ }
113250}
114251
115252/* Everything necessary to release access to the device.
116253 *
117- * For now, this means releasing the lock.
254+ * This means (optionally) putting the device into deep power-down
255+ * mode, and releasing the lock.
118256 */
119257static void release_device (struct device * dev )
120258{
259+ if (IS_ENABLED (CONFIG_SPI_NOR_IDLE_IN_DPD )) {
260+ enter_dpd (dev );
261+ }
262+
121263 if (IS_ENABLED (CONFIG_MULTITHREADING )) {
122264 struct spi_nor_data * const driver_data = dev -> driver_data ;
123265
@@ -348,11 +490,19 @@ static int spi_nor_configure(struct device *dev)
348490 data -> spi_cfg .cs = & data -> cs_ctrl ;
349491#endif /* DT_INST_0_JEDEC_SPI_NOR_CS_GPIOS_CONTROLLER */
350492
493+ /* Might be in DPD if system restarted without power cycle. */
494+ exit_dpd (dev );
495+
351496 /* now the spi bus is configured, we can verify the flash id */
352497 if (spi_nor_read_id (dev , params ) != 0 ) {
353498 return - ENODEV ;
354499 }
355500
501+ if (IS_ENABLED (CONFIG_SPI_NOR_IDLE_IN_DPD )
502+ && (enter_dpd (dev ) != 0 )) {
503+ return - ENODEV ;
504+ }
505+
356506 return 0 ;
357507}
358508
0 commit comments