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