1 /*
2  * Copyright (c) 2017-2019, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*!*****************************************************************************
34  *  @file       NVSSPI25X.h
35  *  @brief      Non-Volatile Storage driver implementation
36  *              for SPI flash peripherals
37  *
38  *  # Overview #
39  *
40  *  The NVSSPI25X module allows you to manage SPI flash memory.
41  *  This driver works with most 256 byte/page SPI flash memory devices
42  *  such as:
43  *
44  *      Winbond     W25xx   family
45  *      Macronics   MX25Rxx family
46  *      Micron      N25Qxx  family
47  *
48  *  The SPI flash commands used by this driver are as follows:
49  *
50  *  @code
51  *  #define SPIFLASH_PAGE_WRITE       0x02 // Page Program (up to 256 bytes)
52  *  #define SPIFLASH_READ             0x03 // Read Data
53  *  #define SPIFLASH_READ_STATUS      0x05 // Read Status Register
54  *  #define SPIFLASH_WRITE_ENABLE     0x06 // Write Enable
55  *  #define SPIFLASH_SUBSECTOR_ERASE  0x20 // SubSector (4K bytes) Erase
56  *  #define SPIFLASH_SECTOR_ERASE     0xD8 // Sector (usually 64K bytes) Erase
57  *  #define SPIFLASH_RDP              0xAB // Release from Deep Power Down
58  *  #define SPIFLASH_DP               0xB9 // Deep Power Down
59  *  #define SPIFLASH_MASS_ERASE       0xC7 // Erase entire flash.
60  *  @endcode
61  *
62  *  It is assumed that the SPI flash device used by this driver supports
63  *  the byte programmability of the SPIFLASH_PAGE_WRITE command and that
64  *  write page size is 256 bytes. The erase sector and subsector sizes are
65  *  assumed to be 64K and 4K respectively.
66  *
67  *  The NVS_erase() command will issue a sector or subsector erase command
68  *  based on the input size and offset.
69  *
70  *  The driver must query the SPI flash to ensure that the part is ready before
71  *  commands are issued. If the part responds as busy, the poll function sleeps
72  *  for a number of microseconds determined by the
73  *  #NVSSPI25X_HWAttrs.statusPollDelayUs field. A value of 0 means that the
74  *  driver will continuously poll the external flash until it is ready, which
75  *  may affect other threads ability to execute.
76  *
77  * ## SPI Interface Management ##
78  *
79  *  For each managed flash region, a corresponding SPI instance must be
80  *  provided to the NVSSPI25X driver.
81  *
82  *  The SPI instance can be opened and closed
83  *  internally by the NVSSPI25X driver, or alternatively, a SPI handle can be
84  *  provided to the NVSSPI25X driver, indicating that the SPI instance is being
85  *  opened and closed elsewhere within the application. This mode is useful
86  *  when the SPI bus is share by more than just the SPI flash device.
87  *
88  *  If the SPI instance is to be managed internally by the NVSSPI25X driver, a SPI
89  *  instance index and bit rate must be configured in the region's HWAttrs.
90  *  If the same SPI instance is referenced by multiple flash regions
91  *  the driver will ensure that SPI_open() is invoked only once, and that
92  *  SPI_close() will only be invoked when all flash regions using the SPI
93  *  instance have been closed.
94  *
95  *  If the SPI bus that the SPI flash device is on is shared with other
96  *  devices accessed by an application, then the SPI handle used to manage
97  *  a SPI flash region can be provided in the region's HWAttrs "spiHandle"
98  *  field. Keep in mind that the "spiHandle" field is a POINTER to a
99  *  SPI Handle, NOT a SPI Handle. This allows the user to simply initialize
100  *  this field with the name of the global variable used for the SPI handle.
101  *  In this mode, the user MUST open the SPI instance prior to opening the NVS
102  *  region instance so that the referenced spiHandle is valid.
103  *
104  *  By default, the "spiHandle" field is set to NULL, indicating that the user
105  *  expects the NVS driver to open and close the SPI instance internally using
106  *  the 'spiIndex' and 'spiBitRate' provided in the HWAttrs.
107  *
108  *  ## @anchor SPI_CS_MGMT SPI Flash Chip Select Management ##
109  *
110  *  ### Option 1: NVSSPI25X Driver Manages Chip Select ###
111  *  By default, the NVSSPI25X driver will assert and de-assert a GPIO
112  *  driver managed pin to select the SPI flash device before and after
113  *  each SPI transfer to and from the device.
114  *
115  *  To enable this behavior, a valid GPIO driver instance index must be
116  *  provided in the NVS region's [spiCsnGpioIndex]
117  *  (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex) field of the
118  *  NVSSPI25X_HWAttrs structure. The corresponding GPIO pin will be
119  *  configured at runtime by the NVSSPI25X driver as "GPIO_CFG_OUT_STD"
120  *  and assertion of this pin is assumed to be active LOW.
121  *
122  *  ### Option 2: SPI Driver Manages Chip Select ###
123  *  Some SPI peripherals can be configured to manage their own chip
124  *  select. Setting the [spiCsnGpioIndex]
125  *   (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex) field of the NVSSPI25X_HWAttrs
126  *  structure to #NVSSPI25X_SPI_MANAGES_CS informs the NVSSPI25X driver
127  *  that the SPI peripheral used by the NVS driver has been configured
128  *  that way.
129  *
130  *  ### Option 3: User Manages Chip Select ###
131  *  Alternatively, the user can manage the assertion and de-assertion of
132  *  the SPI flash chip select entirely themselves by providing implementations
133  *  of the following 4 APIs in their application code:
134  *
135  *  @code
136  *  void NVSSPI25X_initSpiCs(NVS_Handle nvsHandle, uint16_t csId);
137  *  @endcode
138  *  - This function is invoked within the NVS_open() API and is where the
139  *    user should do whatever is required to initialize the hardware
140  *    used for asserting and de-assering the SPI chip select signal.
141  *  - The 'nvsHandle` argument is the NVS handle associated with the
142  *    corresponding NVS region.
143  *  - The 'csId' argument passed to this API is a copy of the [spiCsnGpioIndex]
144  *    (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex)
145  *    field of the corresponding NVS region's NVSSPI25X_HWAttrs structure.
146  *
147  *  @code
148  *  void NVSSPI25X_deinitSpiCs(NVS_Handle nvsHandle, uint16_t csId);
149  *  @endcode
150  *  - This function is invoked within the NVS_close() API and is where the
151  *    user should do whatever is required to de-initialize the hardware
152  *    used for asserting and de-assering the SPI chip select signal.
153  *  - The 'nvsHandle` argument is the NVS handle associated with the
154  *    corresponding NVS region.
155  *  - The 'csId' argument passed to this API is a copy of the [spiCsnGpioIndex]
156  *    (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex)
157  *    field of the corresponding NVS region's NVSSPI25X_HWAttrs structure.
158  *
159  *  @code
160  *  void NVSSPI25X_assertSpiCs(NVS_Handle nvsHandle, uint16_t csId);
161  *  @endcode
162  *  - This function is called PRIOR to every SPI transfer to and from the SPI
163  *    flash device performed by the NVSSPI25X driver. The user code should
164  *    perform the corresponding action required to select the SPI flash
165  *    device to prepare for the SPI transfer.
166  *  - The 'nvsHandle` argument is the NVS handle associated with the
167  *    corresponding NVS region.
168  *  - The 'csId' argument passed to this API is a copy of the [spiCsnGpioIndex]
169  *    (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex)
170  *    field of the corresponding NVS region's NVSSPI25X_HWAttrs structure.
171  *
172  *  @code
173  *  void NVSSPI25X_deassertSpiCs(NVS_Handle nvsHandle, uint16_t csId);
174  *  @endcode
175  *  - This function is called AFTER every SPI transfer to and from the SPI
176  *    flash device performed by the NVSSPI25X driver. The user code should
177  *    perform the corresponding action required to de-select the SPI flash
178  *    device.
179  *    following the SPI transfer.
180  *  - The 'nvsHandle` argument is the NVS handle associated with the
181  *    corresponding NVS region.
182  *  - The 'csId' argument passed to this API is a copy of the [spiCsnGpioIndex]
183  *    (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex)
184  *    field of the corresponding NVS region's NVSSPI25X_HWAttrs structure.
185  *
186  *  @warning  All 4 of the above APIs must be provided by the user if this
187  *  option is used, otherwise default internal implementations of the APIs
188  *  will be called that will likely lead to application failure.
189  */
190 
191 #ifndef ti_drivers_nvs_NVSSPI25X__include
192 #define ti_drivers_nvs_NVSSPI25X__include
193 
194 #include <stdint.h>
195 #include <stdbool.h>
196 
197 #include <ti/drivers/SPI.h>
198 
199 #if defined (__cplusplus)
200 extern "C" {
201 #endif
202 
203 /*!
204  *  @brief Command to perform mass erase of entire flash
205  *
206  *  As this command can erase flash memory outside the region associated
207  *  with the NVS_Handle passed to the control command, the user must
208  *  carefully orchestrate the use of the command.
209  *
210  *  Mass Erase is the only control command supported.
211  */
212 #define NVSSPI25X_CMD_MASS_ERASE        (NVS_CMD_RESERVED + 0)
213 
214 /*!
215  *  @brief Disable internal management of SPI chip select
216  *
217  *  Some SPI peripherals can be configured to manage their own chip
218  *  select. Setting the [spiCsnGpioIndex]
219  *  (@ref NVSSPI25X_HWAttrs.spiCsnGpioIndex) field of the NVSSPI25X_HWAttrs
220  *  structure to #NVSSPI25X_SPI_MANAGES_CS informs the NVSSPI25X driver
221  *  that the SPI peripheral used by the NVS driver is configured
222  *  to manage its own chip select signal.
223  */
224 #define NVSSPI25X_SPI_MANAGES_CS        ((uint16_t)(~0))
225 
226 /*!
227  *  @internal @brief NVS function pointer table
228  *
229  *  'NVSSPI25X_fxnTable' is a fully populated function pointer table
230  *  that can be referenced in the NVS_config[] array entries.
231  *
232  *  Users can minimize their application code size by providing their
233  *  own custom NVS function pointer table that contains only those APIs
234  *  used by the application.
235  */
236 extern const NVS_FxnTable NVSSPI25X_fxnTable;
237 
238 /*!
239  *  @brief      NVSSPI25X attributes
240  *
241  *  The 'regionBaseOffset' is the offset, in bytes, from the base of the
242  *  SPI flash, of the flash region to be managed.
243  *
244  *  The 'regionSize' must be an integer multiple of the flash sector size.
245  *
246  *  The 'sectorSize' is SPI flash device specific. This parameter should
247  *  correspond to the number of bytes erased when the
248  *  'SPIFLASH_SUBSECTOR_ERASE' (0x20) command is issued to the device.
249  *
250  *  The 'verifyBuf' and 'verifyBufSize' parameters are used by the
251  *  NVS_write() command when either 'NVS_WRITE_PRE_VERIFY' or
252  *  'NVS_WRITE_POST_VERIFY' functions are requested in the 'flags'
253  *  argument. The 'verifyBuf' is used to successively read back portions
254  *  of the flash to compare with the data being written to it.
255  *
256  *  @code
257  *  //
258  *  // Only one region write operation is performed at a time
259  *  // so a single verifyBuf can be shared by all the regions.
260  *  //
261  *  uint8_t verifyBuf[256];
262  *
263  *  NVSSPI25X_HWAttrs nvsSPIHWAttrs[2] = {
264  *      //
265  *      // region 0 is 1 flash sector in length.
266  *      //
267  *      {
268  *          .regionBaseOffset = 0,
269  *          .regionSize = 4096,
270  *          .sectorSize = 4096,
271  *          .verifyBuf = verifyBuf;
272  *          .verifyBufSize = 256;
273  *          .spiHandle = NULL,
274  *          .spiIndex = 0,
275  *          .spiBitRate = 40000000,
276  *          .spiCsnGpioIndex = 12,
277  *      },
278  *      //
279  *      // region 1 is 3 flash sectors in length.
280  *      //
281  *      {
282  *          .regionBaseOffset = 4096,
283  *          .regionSize = 4096 * 3,
284  *          .sectorSize = 4096,
285  *          .verifyBuf = verifyBuf;     // use shared verifyBuf
286  *          .verifyBufSize = 256;
287  *          .spiHandle = NULL,
288  *          .spiIndex = 0,
289  *          .spiBitRate = 40000000,
290  *          .spiCsnGpioIndex = 12,
291  *      }
292  *  };
293  *  @endcode
294  */
295 typedef struct
296 {
297     size_t      regionBaseOffset;   /*!< Offset from base of SPI flash */
298     size_t      regionSize;         /*!< The size of the region in bytes */
299     size_t      sectorSize;         /*!< Erase sector size */
300     uint8_t     *verifyBuf;         /*!< Write Pre/Post verify buffer */
301     size_t      verifyBufSize;      /*!< Write Pre/Post verify buffer size */
302     SPI_Handle  *spiHandle;         /*!< ptr to SPI handle if provided by user. */
303     uint16_t    spiIndex;           /*!< SPI instance index from Board file */
304     uint32_t    spiBitRate;         /*!< SPI bit rate in Hz */
305     /*! @brief SPI Flash Chip Select GPIO index
306 
307         This field should be set to either an index within the
308         GPIO driver's GPIO_Config table, or to #NVSSPI25X_SPI_MANAGES_CS.
309         see [SPI Flash Chip Select Management] (@ref SPI_CS_MGMT) for more
310         details.
311     */
312     uint16_t    spiCsnGpioIndex;
313     /*! @brief External Flash Status Poll Delay
314      *
315      * This field determines how many microseconds the driver waits after
316      * querying the external flash status. Increasing this value can help
317      * mitigate CPU starvation if the external flash is busy for long periods
318      * of time, but may also result in increased latency.
319      */
320     uint32_t    statusPollDelayUs;
321 } NVSSPI25X_HWAttrs;
322 
323 /*
324  *  @brief      NVSSPI25X Object
325  *
326  *  The application must not access any member variables of this structure!
327  */
328 typedef struct
329 {
330     bool        opened;             /* Has this region been opened */
331     SPI_Handle  spiHandle;
332     size_t      sectorBaseMask;
333 } NVSSPI25X_Object;
334 
335 /*
336  *  @cond NODOC
337  *  NVSSPI25X driver public APIs
338  */
339 
340 extern void         NVSSPI25X_close(NVS_Handle handle);
341 extern int_fast16_t NVSSPI25X_control(NVS_Handle handle, uint_fast16_t cmd,
342                         uintptr_t arg);
343 extern int_fast16_t NVSSPI25X_erase(NVS_Handle handle, size_t offset,
344                         size_t size);
345 extern void         NVSSPI25X_getAttrs(NVS_Handle handle, NVS_Attrs *attrs);
346 extern void         NVSSPI25X_init();
347 extern int_fast16_t NVSSPI25X_lock(NVS_Handle handle, uint32_t timeout);
348 extern NVS_Handle   NVSSPI25X_open(uint_least8_t index, NVS_Params *params);
349 extern int_fast16_t NVSSPI25X_read(NVS_Handle handle, size_t offset,
350                         void *buffer, size_t bufferSize);
351 extern void         NVSSPI25X_unlock(NVS_Handle handle);
352 extern int_fast16_t NVSSPI25X_write(NVS_Handle handle, size_t offset,
353                         void *buffer, size_t bufferSize, uint_fast16_t flags);
354 /*
355  *  Weakly defined APIs that can be overridden by the user
356  */
357 extern void         NVSSPI25X_initSpiCs(NVS_Handle spiHandle, uint16_t csId);
358 extern void         NVSSPI25X_deinitSpiCs(NVS_Handle spiHandle, uint16_t csId);
359 extern void         NVSSPI25X_assertSpiCs(NVS_Handle spiHandle, uint16_t csId);
360 extern void         NVSSPI25X_deassertSpiCs(NVS_Handle spiHandle, uint16_t csId);
361 
362 /*! @endcond */
363 
364 #if defined (__cplusplus)
365 }
366 #endif /* defined (__cplusplus) */
367 
368 /** @}*/
369 #endif /* ti_drivers_nvs_NVSSPI25X__include */
370