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