1 /**
2 ******************************************************************************
3 * @file stm32h5xx_util_i3c.c
4 * @author MCD Application Team
5 * @brief This utility help to calculate the different I3C Timing.
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2022 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18
19 /* Includes ------------------------------------------------------------------*/
20 #include "stm32h5xx_util_i3c.h"
21
22 /** @addtogroup I3C_Utility
23 * @{
24 */
25
26 /* Private macros ------------------------------------------------------------*/
27 #define DIV_ROUND_CLOSEST(x, d) (((x) + ((d) / 2U)) / (d))
28
29 /* Private Constants ---------------------------------------------------------*/
30 #define SEC210PSEC (uint64_t)100000000000 /*!< 10ps, to take two decimal float of ns calculation */
31 #define TI3CH_MIN 3200U /*!< Open drain & push pull SCL high min, 32ns */
32 #define TI3CH_OD_MAX 4100U /*!< Open drain SCL high max, 41 ns */
33 #define TI3CL_OD_MIN 20000U /*!< Open drain SCL low min, 200 ns */
34 #define TFMPL_OD_MIN 50000U /*!< Fast Mode Plus Open drain SCL low min, 500 ns */
35 #define TFML_OD_MIN 130000U /*!< Fast Mode Open drain SCL low min, 1300 ns */
36 #define TFM_MIN 250000U /*!< Fast Mode, period min for ti3cclk, 2.5us */
37 #define TSM_MIN 1000000U /*!< Standard Mode, period min for ti3cclk, 10us */
38 #define TI3C_CAS_MIN 3840U /*!< time SCL after START min, 38.4 ns */
39 #define TCAPA 35000U /*!< capacitor effect Value measure on Nucleo around 350ns */
40
41 #define BUS_I2Cx_FREQUENCY 100000U /*!< Frequency of I2Cn = 100 KHz*/
42 /* Private Types -------------------------------------------------------------*/
43 /* Private Private Constants -------------------------------------------------*/
44 /* Private variables ---------------------------------------------------------*/
45 /* Private function prototypes -----------------------------------------------*/
46 /* Exported functions ------------------------------------------------------- */
47 /**
48 * @brief Calculate the I3C Controller timing according current I3C clock source and required I3C bus clock.
49 * @param pConfig : [OUT] Pointer to an LL_I3C_CtrlBusConfTypeDef structure that contains
50 * the configuration information for the specified I3C.
51 * @param clockSrcFreq : [IN] I3C clock source in Hz.
52 * @param i3cFreq : [IN] I3C required bus clock in Hz.
53 * @param i2cFreq : [IN] I2C required bus clock in Hz.
54 * @param dutyCycle : [IN] I3C duty cycle for Pure I3C bus or I2C duty cycle for Mixed bus in purcent
55 * Duty cycle must be lower or equal 50 purcent.
56 * @param busType : [IN] Bus configuration type. It can be one value of @ref I3C_BUS_TYPE.
57 * @retval An ErrorStatus enumeration value:
58 * - SUCCESS: Timing calculation successfully
59 * - ERROR: Parameters or timing calculation error
60 */
I3C_CtrlTimingComputation(LL_I3C_CtrlBusConfTypeDef * pConfig,uint32_t clockSrcFreq,uint32_t i3cFreq,uint32_t i2cFreq,uint32_t dutyCycle,uint32_t busType)61 ErrorStatus I3C_CtrlTimingComputation(LL_I3C_CtrlBusConfTypeDef *pConfig, uint32_t clockSrcFreq, uint32_t i3cFreq,
62 uint32_t i2cFreq, uint32_t dutyCycle, uint32_t busType)
63 {
64 ErrorStatus status = SUCCESS;
65
66 /* MIPI Standard constants */
67 /* I3C: Open drain & push pull SCL high min, tDIG_H & tDIG_H_MIXED: 32 ns */
68 uint32_t ti3ch_min = TI3CH_MIN;
69
70 /* I3C: Open drain SCL high max, t_HIGH: 41 ns */
71 uint32_t ti3ch_od_max = TI3CH_OD_MAX;
72
73 /* I3C: Open drain SCL high max, tHIGH: 41 ns (Ti3ch_od_max= 410)
74 I3C (pure bus): Open drain SCL low min, tLOW_OD: 200 ns */
75 uint32_t ti3cl_od_min = TI3CL_OD_MIN;
76
77 /* I3C (mixed bus): Open drain SCL low min,
78 tLOW: 500 ns (FM+ I2C on the bus)
79 tLOW: 1300 ns (FM I2C on the bus) */
80 uint32_t tfmpl_od_min = TFMPL_OD_MIN;
81 uint32_t tfml_od_min = TFML_OD_MIN;
82
83 /* I2C: min ti3cclk
84 fSCL: 1 MHz (FM+)
85 fSCL: 100 kHz (SM) */
86 uint32_t tfm_min = TFM_MIN;
87 uint32_t tsm_min = TSM_MIN;
88
89 /* I3C: time SCL after START min, Tcas: 38,4 ns */
90 uint32_t ti3c_cas_min = TI3C_CAS_MIN;
91
92 /* Period Clock source */
93 uint32_t ti3cclk = 0U;
94
95 /* I3C: Push pull period */
96 uint32_t ti3c_pp_min = 0U;
97
98 /* I2C: Open drain period */
99 uint32_t ti2c_od_min = 0U;
100
101 /* Time for SDA rise to 70% VDD from GND, capacitor effect */
102 /* Value measure on Nucleo around 350ns */
103 uint32_t tcapa = TCAPA;
104
105 /* Compute variable */
106 uint32_t sclhi3c;
107 uint32_t scllpp;
108 uint32_t scllod;
109 uint32_t sclhi2c;
110 uint32_t oneus;
111 uint32_t free;
112 uint32_t sdahold;
113
114 /* Verify Parameters */
115 if (((clockSrcFreq == 0U) || (i3cFreq == 0U)) && (busType == I3C_PURE_I3C_BUS))
116 {
117 status = ERROR;
118 }
119
120 if (((clockSrcFreq == 0U) || (i3cFreq == 0U) || (i2cFreq == 0U)) && (busType == I3C_MIXED_BUS))
121 {
122 status = ERROR;
123 }
124
125 if (status == SUCCESS)
126 {
127 /* Period Clock source */
128 ti3cclk = (uint32_t)((SEC210PSEC + ((uint64_t)clockSrcFreq / (uint64_t)2)) / (uint64_t)clockSrcFreq);
129
130 if ((dutyCycle > 50U) || (ti3cclk == 0U))
131 {
132 status = ERROR;
133 }
134 }
135
136 if ((status == SUCCESS) && (ti3cclk != 0U))
137 {
138 /* I3C: Push pull period */
139 ti3c_pp_min = (uint32_t)((SEC210PSEC + ((uint64_t)i3cFreq / (uint64_t)2)) / (uint64_t)i3cFreq);
140
141 /* I2C: Open drain period */
142 ti2c_od_min = (uint32_t)((SEC210PSEC + ((uint64_t)i2cFreq / (uint64_t)2)) / (uint64_t)i2cFreq);
143
144 if ((busType != I3C_PURE_I3C_BUS) && (ti2c_od_min > tsm_min))
145 {
146 status = ERROR;
147 }
148 }
149
150 /* SCL Computation */
151 if ((status == SUCCESS) && (ti3cclk != 0U))
152 {
153 /* I3C SCL high level (push-pull & open drain) */
154 if (busType == I3C_PURE_I3C_BUS)
155 {
156 sclhi3c = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti3c_pp_min * dutyCycle, ti3cclk), 100U) - 1U;
157
158 /* Check if sclhi3c < ti3ch_min, in that case calculate sclhi3c based on ti3ch_min */
159 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
160 {
161 sclhi3c = DIV_ROUND_CLOSEST(ti3ch_min, ti3cclk) - 1U;
162
163 /* Check if sclhi3c < ti3ch_min */
164 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
165 {
166 sclhi3c += 1U;
167 }
168
169 scllpp = DIV_ROUND_CLOSEST(ti3c_pp_min, ti3cclk) - (sclhi3c + 1U) - 1U;
170 }
171 else
172 {
173 sclhi3c = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti3c_pp_min * dutyCycle, ti3cclk), 100U) - 1U;
174
175 /* Check if sclhi3c < ti3ch_min */
176 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
177 {
178 sclhi3c += 1U;
179 }
180
181 scllpp = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti3c_pp_min * (100U - dutyCycle), ti3cclk), 100U) - 1U;
182 }
183
184 }
185 else
186 {
187 /* Warning: (sclhi3c + 1) * ti3cclk > Ti3ch_od_max expected */
188 sclhi3c = DIV_ROUND_CLOSEST(ti3ch_od_max, ti3cclk) - 1U;
189
190 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
191 {
192 sclhi3c += 1U;
193 }
194 else if (((sclhi3c + 1U) * ti3cclk) > ti3ch_od_max)
195 {
196 sclhi3c = (ti3ch_od_max / ti3cclk);
197 }
198 else
199 {
200 /* Do nothing, keep sclhi3c as previously calculated */
201 }
202
203 /* I3C SCL low level (push-pull) */
204 /* tscllpp = (scllpp + 1) x ti3cclk */
205 scllpp = DIV_ROUND_CLOSEST((ti3c_pp_min - ((sclhi3c + 1U) * ti3cclk)), ti3cclk) - 1U;
206 }
207
208 /* Check if scllpp is superior at (ti3c_pp_min + 1/2 clock source cycle) */
209 /* Goal is to choice the scllpp approach lowest, to have a value frequency highest approach as possible */
210 uint32_t ideal_scllpp = (ti3c_pp_min - ((sclhi3c + 1U) * ti3cclk));
211 if (((scllpp + 1U) * ti3cclk) >= (ideal_scllpp + (ti3cclk / 2U) + 1U))
212 {
213 scllpp -= 1U;
214 }
215
216 /* Check if scllpp + sclhi3c is inferior at (ti3c_pp_min + 1/2 clock source cycle) */
217 /* Goal is to increase the scllpp, to have a value frequency not out of the clock request */
218 if (((scllpp + sclhi3c + 1U + 1U) * ti3cclk) < (ideal_scllpp + (ti3cclk / 2U) + 1U))
219 {
220 scllpp += 1U;
221 }
222
223 /* I3C SCL low level (pure I3C bus) */
224 if (busType == I3C_PURE_I3C_BUS)
225 {
226 if (ti3c_pp_min < ti3cl_od_min)
227 {
228 scllod = DIV_ROUND_CLOSEST(ti3cl_od_min, ti3cclk) - 1U;
229
230 if (((scllod + 1U) * ti3cclk) < ti3cl_od_min)
231 {
232 scllod += 1U;
233 }
234 }
235 else
236 {
237 scllod = scllpp;
238 }
239
240 /* Verify that SCL Open drain Low duration is superior as SDA rise time 70% */
241 if (((scllod + 1U) * ti3cclk) < tcapa)
242 {
243 scllod = DIV_ROUND_CLOSEST(tcapa, ti3cclk) + 1U;
244 }
245
246 sclhi2c = 0U; /* I2C SCL not used in pure I3C bus */
247 }
248 /* SCL low level on mixed bus (open-drain) */
249 /* I2C SCL high level (mixed bus with I2C) */
250 else
251 {
252 scllod = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti2c_od_min * (100U - dutyCycle), ti3cclk), 100U) - 1U;
253
254 /* Mix Bus Fast Mode plus */
255 if (ti2c_od_min < tfm_min)
256 {
257 if (((scllod + 1U) * ti3cclk) < tfmpl_od_min)
258 {
259 scllod = DIV_ROUND_CLOSEST(tfmpl_od_min, ti3cclk) - 1U;
260 }
261 }
262 /* Mix Bus Fast Mode */
263 else
264 {
265 if (((scllod + 1U) * ti3cclk) < tfml_od_min)
266 {
267 scllod = DIV_ROUND_CLOSEST(tfml_od_min, ti3cclk) - 1U;
268 }
269 }
270
271 sclhi2c = DIV_ROUND_CLOSEST((ti2c_od_min - ((scllod + 1U) * ti3cclk)), ti3cclk) - 1U;
272 }
273
274 /* Clock After Start computation */
275
276 /* I3C pure bus: (Tcas + tcapa)/2 */
277 if (busType == I3C_PURE_I3C_BUS)
278 {
279 free = DIV_ROUND_CLOSEST((ti3c_cas_min + tcapa), (2U * ti3cclk)) + 1U;
280 }
281 /* I3C, I2C mixed: (scllod + tcapa)/2 */
282 else
283 {
284 free = DIV_ROUND_CLOSEST((((scllod + 1U) * ti3cclk) + tcapa), (2U * ti3cclk));
285 }
286
287 /* One cycle hold time addition */
288 /* By default 1/2 cycle: must be > 3 ns */
289 if (ti3cclk > 600U)
290 {
291 sdahold = 0U;
292 }
293 else
294 {
295 sdahold = 1U;
296 }
297
298 /* 1 microsecond reference */
299 oneus = DIV_ROUND_CLOSEST(100000U, ti3cclk) - 2U;
300
301 if ((scllpp > 0xFFU) || (sclhi3c > 0xFFU) || (scllod > 0xFFU) || (sclhi2c > 0xFFU) || \
302 (free > 0xFFU) || (oneus > 0xFFU))
303 {
304 /* Case of value is over 8bits, issue may be due to clocksource have a rate too high for bus clock request */
305 /* Update the return status */
306 status = ERROR;
307 }
308 else
309 {
310 /* SCL configuration */
311 pConfig->SCLPPLowDuration = (uint8_t)scllpp;
312 pConfig->SCLI3CHighDuration = (uint8_t)sclhi3c;
313 pConfig->SCLODLowDuration = (uint8_t)scllod;
314 pConfig->SCLI2CHighDuration = (uint8_t)sclhi2c;
315
316 /* Free, Idle and SDA hold time configuration */
317 pConfig->BusFreeDuration = (uint8_t)free;
318 pConfig->BusIdleDuration = (uint8_t)oneus;
319 pConfig->SDAHoldTime = (uint32_t)(sdahold << I3C_TIMINGR1_SDA_HD_Pos);
320 }
321 }
322
323 return status;
324 }
325
326 /**
327 * @brief Calculate the I3C Controller timing according current I3C clock source and required I3C bus clock.
328 * @param pConfig : [OUT] Pointer to an LL_I3C_TgtBusConfTypeDef structure that contains
329 * the configuration information for the specified I3C.
330 * @param clockSrcFreq : [IN] I3C clock source in Hz.
331 * @retval An ErrorStatus enumeration value:
332 * - SUCCESS: Timing calculation successfully
333 * - ERROR: Parameters or timing calculation error
334 */
I3C_TgtTimingComputation(LL_I3C_TgtBusConfTypeDef * pConfig,uint32_t clockSrcFreq)335 ErrorStatus I3C_TgtTimingComputation(LL_I3C_TgtBusConfTypeDef *pConfig, uint32_t clockSrcFreq)
336 {
337 ErrorStatus status = SUCCESS;
338 uint32_t oneus;
339 uint32_t ti3cclk = 0U;
340
341 /* Verify Parameters */
342 if (clockSrcFreq == 0U)
343 {
344 status = ERROR;
345 }
346
347 if (status == SUCCESS)
348 {
349 /* Period Clock source */
350 ti3cclk = (uint32_t)((SEC210PSEC + ((uint64_t)clockSrcFreq / (uint64_t)2)) / (uint64_t)clockSrcFreq);
351
352 /* Verify Parameters */
353 if (ti3cclk == 0U)
354 {
355 status = ERROR;
356 }
357 }
358
359 if ((status == SUCCESS) && (ti3cclk != 0U))
360 {
361 /* 1 microsecond reference */
362 oneus = DIV_ROUND_CLOSEST(100000U, ti3cclk) - 2U;
363
364 /* Bus available time configuration */
365 pConfig->BusAvailableDuration = (uint8_t)oneus;
366 }
367
368 return status;
369 }
370 /* Private functions ---------------------------------------------------------*/
371 /**
372 * @}
373 */
374