1 /***************************************************************************//**
2  * @file
3  * @brief QSPI Octal-SPI Flash Controller API
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "em_qspi.h"
32 
33 #if defined(QSPI_COUNT) && (QSPI_COUNT > 0)
34 
35 #include "sl_assert.h"
36 
37 /* *INDENT-OFF* */
38 /***************************************************************************//**
39  * @addtogroup qspi QSPI Octal-SPI
40  * @brief QSPI Octal-SPI Controller API
41  * @details
42  *  These QSPI functions provide basic support for using the QSPI peripheral
43  *  in the following configurations:
44  *  @li @b Direct Read/Write, used for memory mapped access to external
45  *    memory.
46  *  @li @b STIG Command, used for configuring and executing commands on the
47  *    external memory device.
48  *
49  *  Indirect read/write, PHY configuration, and Execute-In-Place (XIP)
50  *  configurations are not supported.
51  *
52  * The example below shows how to set up the QSPI for direct read and write
53  * operation:
54  * @code
55    CMU_ClockEnable(cmuClock_GPIO, true);
56    CMU_ClockEnable(cmuClock_QSPI0, true);
57 
58    QSPI_Init_TypeDef initQspi = QSPI_INIT_DEFAULT;
59    QSPI_Init(QSPI0, &initQspi);
60 
61    // Configure QSPI pins.
62    GPIO_PinModeSet(EXTFLASH_PORT_CS,   EXTFLASH_PIN_CS,   gpioModePushPull, 0);
63    GPIO_PinModeSet(EXTFLASH_PORT_SCLK, EXTFLASH_PIN_SCLK, gpioModePushPull, 0);
64    GPIO_PinModeSet(EXTFLASH_PORT_DQ0,  EXTFLASH_PIN_DQ0,  gpioModePushPull, 0);
65    GPIO_PinModeSet(EXTFLASH_PORT_DQ1,  EXTFLASH_PIN_DQ1,  gpioModePushPull, 0);
66    GPIO_PinModeSet(EXTFLASH_PORT_DQ2,  EXTFLASH_PIN_DQ2,  gpioModePushPull, 0);
67    GPIO_PinModeSet(EXTFLASH_PORT_DQ3,  EXTFLASH_PIN_DQ3,  gpioModePushPull, 0);
68 
69    // Configure QSPI routing to GPIO.
70    QSPI0->ROUTELOC0 = EXTFLASH_QSPI_LOC;
71    QSPI0->ROUTEPEN  = QSPI_ROUTEPEN_SCLKPEN
72                       | EXTFLASH_QSPI_CSPEN
73                       | QSPI_ROUTEPEN_DQ0PEN
74                       | QSPI_ROUTEPEN_DQ1PEN
75                       | QSPI_ROUTEPEN_DQ2PEN
76                       | QSPI_ROUTEPEN_DQ3PEN;
77 
78    // Configure the direct read.
79    QSPI_ReadConfig_TypeDef readConfig = QSPI_READCONFIG_DEFAULT;
80 
81    readConfig.dummyCycles  = 8;
82    readConfig.opCode       = 0x6B;
83    readConfig.instTransfer = qspiTransferSingle;
84    readConfig.addrTransfer = qspiTransferSingle;
85    readConfig.dataTransfer = qspiTransferQuad;
86 
87    QSPI_ReadConfig(QSPI0, &readConfig);
88 
89    // Configure the direct write.
90    QSPI_WriteConfig_TypeDef writeConfig = QSPI_WRITECONFIG_DEFAULT;
91 
92    writeConfig.dummyCycles  = 0;
93    writeConfig.opCode       = 0x38;
94    writeConfig.addrTransfer = qspiTransferQuad;
95    writeConfig.dataTransfer = qspiTransferQuad;
96    writeConfig.autoWEL      = true;
97 
98    QSPI_WriteConfig(QSPI0, &writeConfig);@endcode
99  *
100  * To configure an external flash, commands can be set up and executed using the
101  * Software Triggered Instruction Generator (STIG) function of the QSPI, as
102  * shown in the example below:
103  * @code
104    uint8_t status;
105    QSPI_StigCmd_TypeDef stigCmd = {0};
106    stigCmd.cmdOpcode = EXTFLASH_OPCODE_READ_STATUS;
107    stigCmd.readDataSize = 1;
108    stigCmd.readBuffer = &status;
109    QSPI_ExecStigCmd(QSPI0, &stigCmd);@endcode
110  * @{
111  ******************************************************************************/
112 /* *INDENT-OFF* */
113 
114 /*******************************************************************************
115  **************************   GLOBAL FUNCTIONS   *******************************
116  ******************************************************************************/
117 
118 /***************************************************************************//**
119  * @brief
120  *   Initialize QSPI.
121  *
122  * @param[in] qspi
123  *   A pointer to the QSPI peripheral register block.
124  *
125  * @param[in] init
126  *   A pointer to the initialization structure used to configure QSPI.
127  ******************************************************************************/
QSPI_Init(QSPI_TypeDef * qspi,const QSPI_Init_TypeDef * init)128 void QSPI_Init(QSPI_TypeDef * qspi, const QSPI_Init_TypeDef * init)
129 {
130   uint32_t divisor;
131 
132   EFM_ASSERT((init->divisor >= 2) && (init->divisor <= 32));
133   divisor = init->divisor / 2 - 1;
134 
135   qspi->CONFIG = (qspi->CONFIG & ~_QSPI_CONFIG_MSTRBAUDDIV_MASK)
136                  | (divisor << _QSPI_CONFIG_MSTRBAUDDIV_SHIFT);
137   QSPI_Enable(qspi, init->enable);
138 }
139 
140 /***************************************************************************//**
141  * @brief
142  *   Configure Read Operations.
143  *
144  * @param[in] qspi
145  *   A pointer to the QSPI peripheral register block.
146  *
147  * @param[in] config
148  *   A pointer to the configuration structure for QSPI read operations.
149  ******************************************************************************/
QSPI_ReadConfig(QSPI_TypeDef * qspi,const QSPI_ReadConfig_TypeDef * config)150 void QSPI_ReadConfig(QSPI_TypeDef * qspi, const QSPI_ReadConfig_TypeDef * config)
151 {
152   EFM_ASSERT(config->dummyCycles < 31);
153 
154   QSPI_WaitForIdle(qspi);
155   qspi->DEVINSTRRDCONFIG = (config->opCode << _QSPI_DEVINSTRRDCONFIG_RDOPCODENONXIP_SHIFT)
156                            | (config->dummyCycles << _QSPI_DEVINSTRRDCONFIG_DUMMYRDCLKCYCLES_SHIFT)
157                            | (config->addrTransfer << _QSPI_DEVINSTRRDCONFIG_ADDRXFERTYPESTDMODE_SHIFT)
158                            | (config->dataTransfer << _QSPI_DEVINSTRRDCONFIG_DATAXFERTYPEEXTMODE_SHIFT)
159                            | (config->instTransfer << _QSPI_DEVINSTRRDCONFIG_INSTRTYPE_SHIFT);
160 }
161 
162 /***************************************************************************//**
163  * @brief
164  *   Configure Write Operations.
165  *
166  * @param[in] qspi
167  *   A pointer to the QSPI peripheral register block.
168  *
169  * @param[in] config
170  *   A pointer to the configuration structure for QSPI write operations.
171  ******************************************************************************/
QSPI_WriteConfig(QSPI_TypeDef * qspi,const QSPI_WriteConfig_TypeDef * config)172 void QSPI_WriteConfig(QSPI_TypeDef * qspi, const QSPI_WriteConfig_TypeDef * config)
173 {
174   EFM_ASSERT(config->dummyCycles < 31);
175 
176   QSPI_WaitForIdle(qspi);
177   qspi->DEVINSTRWRCONFIG = (config->opCode << _QSPI_DEVINSTRWRCONFIG_WROPCODE_SHIFT)
178                            | (config->dummyCycles << _QSPI_DEVINSTRWRCONFIG_DUMMYWRCLKCYCLES_SHIFT)
179                            | (config->addrTransfer << _QSPI_DEVINSTRWRCONFIG_ADDRXFERTYPESTDMODE_SHIFT)
180                            | (config->dataTransfer << _QSPI_DEVINSTRWRCONFIG_DATAXFERTYPEEXTMODE_SHIFT)
181                            | ((config->autoWEL ? 0 : 1) << _QSPI_DEVINSTRWRCONFIG_WELDIS_SHIFT);
182 }
183 
184 /***************************************************************************//**
185  * @brief
186  *   Execute a STIG command.
187  *
188  * @details
189  *   STIG is used when the
190  *   application needs to access status registers, configuration registers or
191  *   perform erase functions. STIG commands can be used to perform any
192  *   instruction that the flash device supports.
193  *
194  * @param[in] qspi
195  *   A pointer to the QSPI peripheral register block.
196  *
197  * @param[in] stigCmd
198  *   A pointer to a structure that describes the STIG command.
199  ******************************************************************************/
QSPI_ExecStigCmd(QSPI_TypeDef * qspi,const QSPI_StigCmd_TypeDef * stigCmd)200 void QSPI_ExecStigCmd(QSPI_TypeDef * qspi, const QSPI_StigCmd_TypeDef * stigCmd)
201 {
202   uint32_t i;
203 
204   EFM_ASSERT(stigCmd->addrSize <= 4);
205   EFM_ASSERT(stigCmd->writeDataSize <= 8);
206   EFM_ASSERT(stigCmd->readDataSize <= 8);
207   EFM_ASSERT(stigCmd->dummyCycles < 32);
208 
209   if (stigCmd->writeDataSize) {
210     EFM_ASSERT(stigCmd->writeBuffer);
211   }
212 
213   if (stigCmd->readDataSize) {
214     EFM_ASSERT(stigCmd->readBuffer);
215   }
216 
217   QSPI_WaitForIdle(qspi);
218 
219   qspi->FLASHCMDCTRL = (stigCmd->cmdOpcode << _QSPI_FLASHCMDCTRL_CMDOPCODE_SHIFT)
220                        | (stigCmd->dummyCycles << _QSPI_FLASHCMDCTRL_NUMDUMMYCYCLES_SHIFT);
221 
222   if (stigCmd->writeDataSize) {
223     uint32_t buffer[2] = { 0, 0 };
224     uint8_t * dst = (uint8_t *) buffer;
225     uint8_t * src = stigCmd->writeBuffer;
226 
227     qspi->FLASHCMDCTRL |= QSPI_FLASHCMDCTRL_ENBWRITEDATA
228                           | ((stigCmd->writeDataSize - 1)
229                              << _QSPI_FLASHCMDCTRL_NUMWRDATABYTES_SHIFT);
230 
231     for (i = 0; i < stigCmd->writeDataSize; i++) {
232       dst[i] = src[i];
233     }
234 
235     qspi->FLASHWRDATALOWER = buffer[0];
236     qspi->FLASHWRDATAUPPER = buffer[1];
237   }
238 
239   if (stigCmd->addrSize) {
240     qspi->FLASHCMDCTRL |= QSPI_FLASHCMDCTRL_ENBCOMDADDR
241                           | ((stigCmd->addrSize - 1)
242                              << _QSPI_FLASHCMDCTRL_NUMADDRBYTES_SHIFT);
243     qspi->FLASHCMDADDR = stigCmd->address;
244   }
245 
246   if (stigCmd->modeBitEnable) {
247     qspi->FLASHCMDCTRL |= QSPI_FLASHCMDCTRL_ENBMODEBIT;
248   }
249 
250   if (stigCmd->readDataSize) {
251     qspi->FLASHCMDCTRL |= QSPI_FLASHCMDCTRL_ENBREADDATA
252                           | ((stigCmd->readDataSize - 1)
253                              << _QSPI_FLASHCMDCTRL_NUMRDDATABYTES_SHIFT);
254   }
255 
256   // Start command execution
257   qspi->FLASHCMDCTRL |= QSPI_FLASHCMDCTRL_CMDEXEC;
258 
259   while (qspi->FLASHCMDCTRL & QSPI_FLASHCMDCTRL_CMDEXECSTATUS)
260     ;
261 
262   // Read data if any
263   if (stigCmd->readDataSize) {
264     uint32_t buffer[2] = { 0, 0 };
265     const uint8_t * src = (const uint8_t *)buffer;
266     uint8_t * dst = stigCmd->readBuffer;
267 
268     buffer[0] = qspi->FLASHRDDATALOWER;
269     buffer[1] = qspi->FLASHRDDATAUPPER;
270 
271     for (i = 0; i < stigCmd->readDataSize; i++) {
272       dst[i] = src[i];
273     }
274   }
275 }
276 
277 /** @} (end addtogroup qspi) */
278 
279 #endif /* defined(QSPI_COUNT) && (QSPI_COUNT > 0) */
280