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) 2023 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 STM32H5xx_UTIL_Driver
23 * @{
24 */
25
26 /** @addtogroup UTILITY_I3C
27 * @{
28 */
29
30 #if (defined(USE_HAL_DRIVER) && defined(HAL_I3C_MODULE_ENABLED)) || defined(USE_FULL_LL_DRIVER)
31
32 /* Private typedef ---------------------------------------------------------------------------------------------------*/
33 /* Private define ----------------------------------------------------------------------------------------------------*/
34 /** @defgroup I3C_UTIL_Private_Define I3C Utility Private Define
35 * @{
36 */
37 #define SEC210PSEC (uint64_t)100000000000 /*!< 10ps, to take two decimal float of ns calculation */
38 #define TI3CH_MIN 3200U /*!< Open drain & push pull SCL high min, 32ns */
39 #define TI3CH_OD_MAX 4100U /*!< Open drain SCL high max, 41 ns */
40 #define TI3CL_OD_MIN 20000U /*!< Open drain SCL low min, 200 ns */
41 #define TFMPL_OD_MIN 50000U /*!< Fast Mode Plus Open drain SCL low min, 500 ns */
42 #define TFML_OD_MIN 130000U /*!< Fast Mode Open drain SCL low min, 1300 ns */
43 #define TFM_MIN 250000U /*!< Fast Mode, period min for ti3cclk, 2.5us */
44 #define TSM_MIN 1000000U /*!< Standard Mode, period min for ti3cclk, 10us */
45 #define TI3C_CAS_MIN 3840U /*!< Time SCL after START min, 38.4 ns */
46 #define TCAPA 35000U /*!< Capacitor effect Value measure on Nucleo around 350ns */
47 #define I3C_FREQUENCY_MAX 257000000U /*!< Maximum I3C frequency */
48 /**
49 * @}
50 */
51
52 /* Private macro -----------------------------------------------------------------------------------------------------*/
53 /** @defgroup I3C_UTIL_Private_Macro I3C Utility Private Macro
54 * @{
55 */
56 #define DIV_ROUND_CLOSEST(x, d) (((x) + ((d) / 2U)) / (d))
57 /**
58 * @}
59 */
60
61 /* Private function prototypes ---------------------------------------------------------------------------------------*/
62 /* Exported functions ------------------------------------------------------------------------------------------------*/
63 /** @defgroup I3C_UTIL_Exported_Functions I3C Utility Exported Functions
64 * @{
65 */
66
67 /** @defgroup I3C_UTIL_EF_Computation Computation
68 * @{
69 */
70 /**
71 * @brief Calculate the I3C Controller timing according current I3C clock source and required I3C bus clock.
72 * @param pInputTiming : [IN] Pointer to an I3C_CtrlTimingTypeDef structure that contains
73 * the required parameter for I3C timing computation.
74 * @param pOutputConfig : [OUT] Pointer to an LL_I3C_CtrlBusConfTypeDef structure that contains
75 * the configuration information for the specified I3C.
76 * @retval An ErrorStatus enumeration value:
77 * - SUCCESS: Timing calculation successfully
78 * - ERROR: Parameters or timing calculation error
79 */
I3C_CtrlTimingComputation(const I3C_CtrlTimingTypeDef * pInputTiming,LL_I3C_CtrlBusConfTypeDef * pOutputConfig)80 ErrorStatus I3C_CtrlTimingComputation(const I3C_CtrlTimingTypeDef *pInputTiming,
81 LL_I3C_CtrlBusConfTypeDef *pOutputConfig)
82 {
83 ErrorStatus status = SUCCESS;
84
85 /* MIPI Standard constants */
86 /* I3C: Open drain & push pull SCL high min, tDIG_H & tDIG_H_MIXED: 32 ns */
87 uint32_t ti3ch_min = TI3CH_MIN;
88
89 /* I3C: Open drain SCL high max, t_HIGH: 41 ns */
90 uint32_t ti3ch_od_max = TI3CH_OD_MAX;
91
92 /* I3C: Open drain SCL high max, tHIGH: 41 ns (Ti3ch_od_max= 410)
93 I3C (pure bus): Open drain SCL low min, tLOW_OD: 200 ns */
94 uint32_t ti3cl_od_min = TI3CL_OD_MIN;
95
96 /* I3C (mixed bus): Open drain SCL low min,
97 tLOW: 500 ns (FM+ I2C on the bus)
98 tLOW: 1300 ns (FM I2C on the bus) */
99 uint32_t tfmpl_od_min = TFMPL_OD_MIN;
100 uint32_t tfml_od_min = TFML_OD_MIN;
101
102 /* I2C: min ti3cclk
103 fSCL: 1 MHz (FM+)
104 fSCL: 100 kHz (SM) */
105 uint32_t tfm_min = TFM_MIN;
106 uint32_t tsm_min = TSM_MIN;
107
108 /* I3C: time SCL after START min, Tcas: 38,4 ns */
109 uint32_t ti3c_cas_min = TI3C_CAS_MIN;
110
111 /* Period Clock source */
112 uint32_t ti3cclk = 0U;
113
114 /* I3C: Push pull period */
115 uint32_t ti3c_pp_min = 0U;
116
117 /* I2C: Open drain period */
118 uint32_t ti2c_od_min = 0U;
119
120 /* Time for SDA rise to 70% VDD from GND, capacitor effect */
121 /* Value measure on Nucleo around 350ns */
122 uint32_t tcapa = TCAPA;
123
124 /* Compute variable */
125 uint32_t sclhi3c;
126 uint32_t scllpp;
127 uint32_t scllod;
128 uint32_t sclhi2c;
129 uint32_t oneus;
130 uint32_t free;
131 uint32_t sdahold;
132
133 /* Verify Parameters */
134 if (pInputTiming->clockSrcFreq > I3C_FREQUENCY_MAX)
135 {
136 /* Above this frequency, some timing register parameters are over than field value */
137 status = ERROR;
138 }
139
140 if ((pInputTiming->busType != I3C_PURE_I3C_BUS) && (pInputTiming->busType != I3C_MIXED_BUS))
141 {
142 status = ERROR;
143 }
144
145 if (((pInputTiming->clockSrcFreq == 0U) || (pInputTiming->i3cPPFreq == 0U)) &&
146 (pInputTiming->busType == I3C_PURE_I3C_BUS))
147 {
148 status = ERROR;
149 }
150
151 if (((pInputTiming->clockSrcFreq == 0U) || (pInputTiming->i3cPPFreq == 0U) || (pInputTiming->i2cODFreq == 0U)) &&
152 (pInputTiming->busType == I3C_MIXED_BUS))
153 {
154 status = ERROR;
155 }
156
157 if (status == SUCCESS)
158 {
159 /* Period Clock source */
160 ti3cclk = (uint32_t)((SEC210PSEC + ((uint64_t)pInputTiming->clockSrcFreq / (uint64_t)2)) /
161 (uint64_t)pInputTiming->clockSrcFreq);
162
163 if ((pInputTiming->dutyCycle > 50U) || (ti3cclk == 0U))
164 {
165 status = ERROR;
166 }
167 }
168
169 if ((status == SUCCESS) && (ti3cclk != 0U))
170 {
171 /* I3C: Push pull period */
172 ti3c_pp_min = (uint32_t)((SEC210PSEC + ((uint64_t)pInputTiming->i3cPPFreq / (uint64_t)2)) /
173 (uint64_t)pInputTiming->i3cPPFreq);
174
175 /* I2C: Open drain period */
176 ti2c_od_min = (uint32_t)((SEC210PSEC + ((uint64_t)pInputTiming->i2cODFreq / (uint64_t)2)) /
177 (uint64_t)pInputTiming->i2cODFreq);
178
179 if ((pInputTiming->busType != I3C_PURE_I3C_BUS) && (ti2c_od_min > tsm_min))
180 {
181 status = ERROR;
182 }
183 }
184
185 /* SCL Computation */
186 if ((status == SUCCESS) && (ti3cclk != 0U))
187 {
188 /* I3C SCL high level (push-pull & open drain) */
189 if (pInputTiming->busType == I3C_PURE_I3C_BUS)
190 {
191 sclhi3c = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti3c_pp_min * pInputTiming->dutyCycle, ti3cclk), 100U) - 1U;
192
193 /* Check if sclhi3c < ti3ch_min, in that case calculate sclhi3c based on ti3ch_min */
194 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
195 {
196 sclhi3c = DIV_ROUND_CLOSEST(ti3ch_min, ti3cclk) - 1U;
197
198 /* Check if sclhi3c < ti3ch_min */
199 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
200 {
201 sclhi3c += 1U;
202 }
203
204 scllpp = DIV_ROUND_CLOSEST(ti3c_pp_min, ti3cclk) - (sclhi3c + 1U) - 1U;
205 }
206 else
207 {
208 sclhi3c = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti3c_pp_min * pInputTiming->dutyCycle, ti3cclk), 100U) - 1U;
209
210 /* Check if sclhi3c < ti3ch_min */
211 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
212 {
213 sclhi3c += 1U;
214 }
215
216 scllpp = DIV_ROUND_CLOSEST((ti3c_pp_min - ((sclhi3c + 1U) * ti3cclk) + (ti3cclk / 2U)), ti3cclk) - 1U;
217 }
218
219 }
220 else
221 {
222 /* Warning: (sclhi3c + 1) * ti3cclk > Ti3ch_od_max expected */
223 sclhi3c = DIV_ROUND_CLOSEST(ti3ch_od_max, ti3cclk) - 1U;
224
225 if (((sclhi3c + 1U) * ti3cclk) < ti3ch_min)
226 {
227 sclhi3c += 1U;
228 }
229 else if (((sclhi3c + 1U) * ti3cclk) > ti3ch_od_max)
230 {
231 sclhi3c = (ti3ch_od_max / ti3cclk);
232 }
233 else
234 {
235 /* Do nothing, keep sclhi3c as previously calculated */
236 }
237
238 /* I3C SCL low level (push-pull) */
239 /* tscllpp = (scllpp + 1) x ti3cclk */
240 scllpp = DIV_ROUND_CLOSEST((ti3c_pp_min - ((sclhi3c + 1U) * ti3cclk)), ti3cclk) - 1U;
241 }
242
243 /* Check if scllpp is superior at (ti3c_pp_min + 1/2 clock source cycle) */
244 /* Goal is to choice the scllpp approach lowest, to have a value frequency highest approach as possible */
245 uint32_t ideal_scllpp = (ti3c_pp_min - ((sclhi3c + 1U) * ti3cclk));
246 if (((scllpp + 1U) * ti3cclk) >= (ideal_scllpp + (ti3cclk / 2U) + 1U))
247 {
248 scllpp -= 1U;
249 }
250
251 /* Check if scllpp + sclhi3c is inferior at (ti3c_pp_min + 1/2 clock source cycle) */
252 /* Goal is to increase the scllpp, to have a value frequency not out of the clock request */
253 if (((scllpp + sclhi3c + 1U + 1U) * ti3cclk) < (ideal_scllpp + (ti3cclk / 2U) + 1U))
254 {
255 scllpp += 1U;
256 }
257
258 /* I3C SCL low level (pure I3C bus) */
259 if (pInputTiming->busType == I3C_PURE_I3C_BUS)
260 {
261 if (ti3c_pp_min < ti3cl_od_min)
262 {
263 scllod = DIV_ROUND_CLOSEST(ti3cl_od_min, ti3cclk) - 1U;
264
265 if (((scllod + 1U) * ti3cclk) < ti3cl_od_min)
266 {
267 scllod += 1U;
268 }
269 }
270 else
271 {
272 scllod = scllpp;
273 }
274
275 /* Verify that SCL Open drain Low duration is superior as SDA rise time 70% */
276 if (((scllod + 1U) * ti3cclk) < tcapa)
277 {
278 scllod = DIV_ROUND_CLOSEST(tcapa, ti3cclk) + 1U;
279 }
280
281 sclhi2c = 0U; /* I2C SCL not used in pure I3C bus */
282 }
283 /* SCL low level on mixed bus (open-drain) */
284 /* I2C SCL high level (mixed bus with I2C) */
285 else
286 {
287 scllod = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ti2c_od_min * (100U - pInputTiming->dutyCycle),
288 ti3cclk), 100U) - 1U;
289
290 /* Mix Bus Fast Mode plus */
291 if (ti2c_od_min < tfm_min)
292 {
293 if (((scllod + 1U) * ti3cclk) < tfmpl_od_min)
294 {
295 scllod = DIV_ROUND_CLOSEST(tfmpl_od_min, ti3cclk) - 1U;
296 }
297 }
298 /* Mix Bus Fast Mode */
299 else
300 {
301 if (((scllod + 1U) * ti3cclk) < tfml_od_min)
302 {
303 scllod = DIV_ROUND_CLOSEST(tfml_od_min, ti3cclk) - 1U;
304 }
305 }
306
307 sclhi2c = DIV_ROUND_CLOSEST((ti2c_od_min - ((scllod + 1U) * ti3cclk)), ti3cclk) - 1U;
308 }
309
310 /* Clock After Start computation */
311
312 /* I3C pure bus: (Tcas + tcapa)/2 */
313 if (pInputTiming->busType == I3C_PURE_I3C_BUS)
314 {
315 free = DIV_ROUND_CLOSEST((ti3c_cas_min + tcapa), (2U * ti3cclk)) + 1U;
316 }
317 /* I3C, I2C mixed: (scllod + tcapa)/2 */
318 else
319 {
320 free = DIV_ROUND_CLOSEST((((scllod + 1U) * ti3cclk) + tcapa), (2U * ti3cclk));
321 }
322
323 /* One cycle hold time addition */
324 /* By default 1/2 cycle: must be > 3 ns */
325 if (ti3cclk > 600U)
326 {
327 sdahold = 0U;
328 }
329 else
330 {
331 sdahold = 1U;
332 }
333
334 /* 1 microsecond reference */
335 oneus = DIV_ROUND_CLOSEST(100000U, ti3cclk) - 2U;
336
337 if ((scllpp > 0xFFU) || (sclhi3c > 0xFFU) || (scllod > 0xFFU) || (sclhi2c > 0xFFU) ||
338 (free > 0xFFU) || (oneus > 0xFFU))
339 {
340 /* Case of value is over 8bits, issue may be due to clocksource have a rate too high for bus clock request */
341 /* Update the return status */
342 status = ERROR;
343 }
344 else
345 {
346 /* SCL configuration */
347 pOutputConfig->SCLPPLowDuration = (uint8_t)scllpp;
348 pOutputConfig->SCLI3CHighDuration = (uint8_t)sclhi3c;
349 pOutputConfig->SCLODLowDuration = (uint8_t)scllod;
350 pOutputConfig->SCLI2CHighDuration = (uint8_t)sclhi2c;
351
352 /* Free, Idle and SDA hold time configuration */
353 pOutputConfig->BusFreeDuration = (uint8_t)free;
354 pOutputConfig->BusIdleDuration = (uint8_t)oneus;
355 pOutputConfig->SDAHoldTime = (uint32_t)(sdahold << I3C_TIMINGR1_SDA_HD_Pos);
356 }
357 }
358
359 return status;
360 }
361
362 /**
363 * @brief Calculate the I3C Controller timing according current I3C clock source and required I3C bus clock.
364 * @param pInputTiming : [IN] Pointer to an I3C_TgtTimingTypeDef structure that contains
365 * the required parameter for I3C timing computation.
366 * @param pOutputConfig : [OUT] Pointer to an LL_I3C_TgtBusConfTypeDef structure that contains
367 * the configuration information for the specified I3C.
368 * @retval An ErrorStatus enumeration value:
369 * - SUCCESS: Timing calculation successfully
370 * - ERROR: Parameters or timing calculation error
371 */
I3C_TgtTimingComputation(const I3C_TgtTimingTypeDef * pInputTiming,LL_I3C_TgtBusConfTypeDef * pOutputConfig)372 ErrorStatus I3C_TgtTimingComputation(const I3C_TgtTimingTypeDef *pInputTiming,
373 LL_I3C_TgtBusConfTypeDef *pOutputConfig)
374 {
375 ErrorStatus status = SUCCESS;
376 uint32_t oneus;
377 uint32_t ti3cclk = 0U;
378
379 /* Verify Parameters */
380 if (pInputTiming->clockSrcFreq == 0U)
381 {
382 status = ERROR;
383 }
384
385 if (status == SUCCESS)
386 {
387 /* Period Clock source */
388 ti3cclk = (uint32_t)((SEC210PSEC + ((uint64_t)pInputTiming->clockSrcFreq / (uint64_t)2)) /
389 (uint64_t)pInputTiming->clockSrcFreq);
390
391 /* Verify Parameters */
392 if (ti3cclk == 0U)
393 {
394 status = ERROR;
395 }
396 }
397
398 if ((status == SUCCESS) && (ti3cclk != 0U))
399 {
400 /* 1 microsecond reference */
401 oneus = DIV_ROUND_CLOSEST(100000U, ti3cclk) - 2U;
402
403 /* Bus available time configuration */
404 pOutputConfig->BusAvailableDuration = (uint8_t)oneus;
405 }
406
407 return status;
408 }
409 /**
410 * @}
411 */
412 /**
413 * @}
414 */
415
416 /* Private functions ---------------------------------------------------------*/
417
418 #endif /* (defined(USE_HAL_DRIVER) && defined(HAL_I3C_MODULE_ENABLED)) || defined(USE_FULL_LL_DRIVER) */
419 /**
420 * @}
421 */
422
423 /**
424 * @}
425 */
426