1 /*
2  * Copyright (c) 2022,  NXP
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include "fsl_power.h"
7 #include "flash_clock_setup.h"
8 
9 #define FLEXSPI_DLL_LOCK_RETRY (10)
10 
flash_deinit(FLEXSPI_Type * base)11 static void flash_deinit(FLEXSPI_Type *base)
12 {
13 	/* Enable FLEXSPI clock again */
14 	CLKCTL0->PSCCTL0_SET = CLKCTL0_PSCCTL0_SET_FLEXSPI0_OTFAD_CLK_MASK;
15 
16 	/* Enable FLEXSPI module */
17 	base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK;
18 
19 	/* Wait until FLEXSPI is not busy */
20 	while (!((base->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) &&
21 		 (base->STS0 & FLEXSPI_STS0_SEQIDLE_MASK))) {
22 	}
23 	/* Disable module during the reset procedure */
24 	base->MCR0 |= FLEXSPI_MCR0_MDIS_MASK;
25 }
26 
flash_init(FLEXSPI_Type * base)27 static void flash_init(FLEXSPI_Type *base)
28 {
29 	uint32_t status;
30 	uint32_t lastStatus;
31 	uint32_t retry;
32 
33 	/* If serial root clock is >= 100 MHz, DLLEN set to 1, OVRDEN set to 0,
34 	 * then SLVDLYTARGET setting of 0x0 is recommended.
35 	 */
36 	base->DLLCR[0] = 0x1U;
37 
38 	/* Enable FLEXSPI module */
39 	base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK;
40 
41 	base->MCR0 |= FLEXSPI_MCR0_SWRESET_MASK;
42 	while (base->MCR0 & FLEXSPI_MCR0_SWRESET_MASK) {
43 	}
44 
45 	/* Need to wait DLL locked if DLL enabled */
46 	if (0U != (base->DLLCR[0] & FLEXSPI_DLLCR_DLLEN_MASK)) {
47 		lastStatus = base->STS2;
48 		retry = FLEXSPI_DLL_LOCK_RETRY;
49 		/* Wait slave delay line locked and slave reference delay line locked. */
50 		do {
51 			status = base->STS2;
52 			if ((status & (FLEXSPI_STS2_AREFLOCK_MASK | FLEXSPI_STS2_ASLVLOCK_MASK)) ==
53 			    (FLEXSPI_STS2_AREFLOCK_MASK | FLEXSPI_STS2_ASLVLOCK_MASK)) {
54 				/* Locked */
55 				retry = 100;
56 				break;
57 			} else if (status == lastStatus) {
58 				/* Same delay cell number in calibration */
59 				retry--;
60 			} else {
61 				retry = FLEXSPI_DLL_LOCK_RETRY;
62 				lastStatus = status;
63 			}
64 		} while (retry > 0);
65 		/* According to ERR011377, need to delay at least 100 NOPs to ensure
66 		 * the DLL is locked.
67 		 */
68 		for (; retry > 0U; retry--) {
69 			__NOP();
70 		}
71 	}
72 }
73 
74 /*
75  * flexspi_set_clock run in RAM used to configure FlexSPI clock source and divider
76  * when XIP.
77  */
flexspi_setup_clock(FLEXSPI_Type * base,uint32_t src,uint32_t divider)78 void flexspi_setup_clock(FLEXSPI_Type *base, uint32_t src, uint32_t divider)
79 {
80 	if (base == FLEXSPI0) {
81 		if ((CLKCTL0->FLEXSPI0FCLKSEL != CLKCTL0_FLEXSPI0FCLKSEL_SEL(src)) ||
82 		   ((CLKCTL0->FLEXSPI0FCLKDIV & CLKCTL0_FLEXSPI0FCLKDIV_DIV_MASK) !=
83 		    (divider - 1))) {
84 			/* Always deinit FLEXSPI and init FLEXSPI for the flash to make sure the
85 			 * flash works correctly after the FLEXSPI root clock changed as the
86 			 * default FLEXSPI configuration may does not work for the new root
87 			 * clock frequency.
88 			 */
89 			flash_deinit(base);
90 
91 			/* Disable clock before changing clock source */
92 			CLKCTL0->PSCCTL0_CLR = CLKCTL0_PSCCTL0_CLR_FLEXSPI0_OTFAD_CLK_MASK;
93 			/* Update flexspi clock. */
94 			CLKCTL0->FLEXSPI0FCLKSEL = CLKCTL0_FLEXSPI0FCLKSEL_SEL(src);
95 			/* Reset the divider counter */
96 			CLKCTL0->FLEXSPI0FCLKDIV |= CLKCTL0_FLEXSPI0FCLKDIV_RESET_MASK;
97 			CLKCTL0->FLEXSPI0FCLKDIV = CLKCTL0_FLEXSPI0FCLKDIV_DIV(divider - 1);
98 			while ((CLKCTL0->FLEXSPI0FCLKDIV) & CLKCTL0_FLEXSPI0FCLKDIV_REQFLAG_MASK) {
99 			}
100 			/* Enable FLEXSPI clock again */
101 			CLKCTL0->PSCCTL0_SET = CLKCTL0_PSCCTL0_SET_FLEXSPI0_OTFAD_CLK_MASK;
102 
103 			flash_init(base);
104 		}
105 	} else if (base == FLEXSPI1) {
106 		if ((CLKCTL0->FLEXSPI1FCLKSEL != CLKCTL0_FLEXSPI1FCLKSEL_SEL(src)) ||
107 		    ((CLKCTL0->FLEXSPI1FCLKDIV & CLKCTL0_FLEXSPI1FCLKDIV_DIV_MASK) !=
108 		     (divider - 1))) {
109 			/* Always deinit FLEXSPI and init FLEXSPI for the flash to make sure the
110 			 * flash works correctly after the FLEXSPI root clock changed as the
111 			 * default FLEXSPI configuration may does not work for the new root
112 			 * clock frequency.
113 			 */
114 			flash_deinit(base);
115 
116 			/* Disable clock before changing clock source */
117 			CLKCTL0->PSCCTL0_CLR = CLKCTL0_PSCCTL0_CLR_FLEXSPI1_CLK_MASK;
118 			/* Update flexspi clock. */
119 			CLKCTL0->FLEXSPI1FCLKSEL = CLKCTL0_FLEXSPI1FCLKSEL_SEL(src);
120 			/* Reset the divider counter */
121 			CLKCTL0->FLEXSPI1FCLKDIV |= CLKCTL0_FLEXSPI1FCLKDIV_RESET_MASK;
122 			CLKCTL0->FLEXSPI1FCLKDIV = CLKCTL0_FLEXSPI1FCLKDIV_DIV(divider - 1);
123 			while ((CLKCTL0->FLEXSPI1FCLKDIV) & CLKCTL0_FLEXSPI1FCLKDIV_REQFLAG_MASK) {
124 			}
125 			/* Enable FLEXSPI clock again */
126 			CLKCTL0->PSCCTL0_SET = CLKCTL0_PSCCTL0_SET_FLEXSPI1_CLK_MASK;
127 
128 			flash_init(base);
129 		}
130 	} else {
131 		return;
132 	}
133 }
134 
135 /* This function is used to change FlexSPI clock to a stable source before clock
136  * sources(Such as PLL and Main clock) updating in case XIP (execute code on
137  * FLEXSPI memory.)
138  */
flexspi_clock_safe_config(void)139 void flexspi_clock_safe_config(void)
140 {
141 	/* Move FLEXSPI clock source from main clock to FRO192M / 2 to avoid instruction/data
142 	 * fetch issue in XIP when updating PLL and main clock.
143 	 */
144 	flexspi_setup_clock(FLEXSPI0, 3U, 2U);
145 }
146