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