1 /*
2  * Copyright (c) 2006-2024 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-06-25     Andeyqi      the first version
9  */
10 #include <rtthread.h>
11 #include <rthw.h>
12 #include <rtdevice.h>
13 #include <board.h>
14 
15 #ifdef BSP_USING_SW_I2C
16 
17 #define LOG_TAG              "drv.i2c"
18 #include <rtdbg.h>
19 
20 /* MCXNXXX config class */
21 struct mcxnxxx_soft_i2c_config
22 {
23     rt_uint8_t scl;
24     rt_uint8_t sda;
25     const char *bus_name;
26 };
27 
28 /* MCXNXXX i2c dirver class */
29 struct mcxnxxx_i2c
30 {
31     struct rt_i2c_bit_ops ops;
32     struct rt_i2c_bus_device i2c2_bus;
33 };
34 
35 #if defined(BSP_USING_SW_I2C0)
36 #define SW_I2C0_BUS_CONFIG                               \
37     {                                                    \
38         .scl = BSP_SW_I2C0_SCL_PIN,                      \
39         .sda = BSP_SW_I2C0_SDA_PIN,                      \
40         .bus_name = "swi2c0",                            \
41     }
42 #endif
43 
44 static const struct mcxnxxx_soft_i2c_config soft_i2c_config[] =
45 {
46 #if defined(BSP_USING_SW_I2C0)
47     SW_I2C0_BUS_CONFIG,
48 #endif
49 };
50 
51 static struct mcxnxxx_i2c i2c_obj[sizeof(soft_i2c_config) / sizeof(soft_i2c_config[0])];
52 
53 /**
54   * @brief  This function initializes the i2c pin.
55   * @param  i2c
56   * @retval None
57   */
mcxnxxx_i2c_gpio_init(struct mcxnxxx_i2c * i2c)58 static void mcxnxxx_i2c_gpio_init(struct mcxnxxx_i2c *i2c)
59 {
60     struct mcxnxxx_soft_i2c_config* cfg = (struct mcxnxxx_soft_i2c_config*)i2c->ops.data;
61 
62     rt_pin_mode(cfg->scl, PIN_MODE_OUTPUT_OD);
63     rt_pin_mode(cfg->sda, PIN_MODE_OUTPUT_OD);
64 
65     rt_pin_write(cfg->scl, PIN_HIGH);
66     rt_pin_write(cfg->sda, PIN_HIGH);
67 }
68 
69 /**
70   * @brief  This function sets the sda pin.
71   * @param  data, state
72   * @retval None
73   */
mcxnxxx_set_sda(void * data,rt_int32_t state)74 static void mcxnxxx_set_sda(void *data, rt_int32_t state)
75 {
76     struct mcxnxxx_soft_i2c_config* cfg = (struct mcxnxxx_soft_i2c_config*)data;
77 
78     rt_pin_mode(cfg->sda, PIN_MODE_OUTPUT_OD);
79 
80     if (state)
81     {
82         rt_pin_write(cfg->sda, PIN_HIGH);
83     }
84     else
85     {
86         rt_pin_write(cfg->sda, PIN_LOW);
87     }
88 }
89 
90 /**
91   * @brief  This function sets the scl pin.
92   * @param  data, state
93   * @retval None
94   */
mcxnxxx_set_scl(void * data,rt_int32_t state)95 static void mcxnxxx_set_scl(void *data, rt_int32_t state)
96 {
97     struct mcxnxxx_soft_i2c_config* cfg = (struct mcxnxxx_soft_i2c_config*)data;
98 
99     rt_pin_mode(cfg->scl, PIN_MODE_OUTPUT_OD);
100 
101     if (state)
102     {
103         rt_pin_write(cfg->scl, PIN_HIGH);
104     }
105     else
106     {
107         rt_pin_write(cfg->scl, PIN_LOW);
108     }
109 }
110 
111 /**
112   * @brief  This function gets the sda pin state.
113   * @param  data
114   * @retval None
115   */
mcxnxxx_get_sda(void * data)116 static rt_int32_t mcxnxxx_get_sda(void *data)
117 {
118     struct mcxnxxx_soft_i2c_config* cfg = (struct mcxnxxx_soft_i2c_config*)data;
119 
120     rt_pin_mode(cfg->sda, PIN_MODE_INPUT);
121 
122     return rt_pin_read(cfg->sda);
123 }
124 
125 
126 /**
127   * @brief  This function gets the scl pin state.
128   * @param  data
129   * @retval None
130   */
mcxnxxx_get_scl(void * data)131 static rt_int32_t mcxnxxx_get_scl(void *data)
132 {
133     struct mcxnxxx_soft_i2c_config* cfg = (struct mcxnxxx_soft_i2c_config*)data;
134 
135     rt_pin_mode(cfg->scl,PIN_MODE_INPUT);
136 
137     return rt_pin_read(cfg->scl);
138 }
139 
140 /**
141   * @brief  The time delay function.
142   * @param  us
143   * @retval None
144   */
mcxnxxx_udelay(rt_uint32_t us)145 static void mcxnxxx_udelay(rt_uint32_t us)
146 {
147     rt_uint32_t frequency = CLOCK_GetCoreSysClkFreq();
148 
149     int i = (frequency/ 4000000 * us);
150     while(i)
151     {
152         i--;
153     }
154 }
155 
156 static const struct rt_i2c_bit_ops mcxnxxx_bit_ops_default =
157 {
158     .data     = RT_NULL,
159     .set_sda  = mcxnxxx_set_sda,
160     .set_scl  = mcxnxxx_set_scl,
161     .get_sda  = mcxnxxx_get_sda,
162     .get_scl  = mcxnxxx_get_scl,
163     .udelay   = mcxnxxx_udelay,
164     .delay_us = 1,
165     .timeout  = 100
166 };
167 
168 /**
169   * @brief  if i2c is locked, this function will unlock it
170   * @param  cfg
171   * @retval RT_EOK indicates successful unlock.
172   */
mcxnxxx_i2c_bus_unlock(const struct mcxnxxx_soft_i2c_config * cfg)173 static rt_err_t mcxnxxx_i2c_bus_unlock(const struct mcxnxxx_soft_i2c_config *cfg)
174 {
175     rt_int32_t i = 0;
176 
177     if (PIN_LOW == rt_pin_read(cfg->sda))
178     {
179         while (i++ < 9)
180         {
181             rt_pin_write(cfg->scl, PIN_HIGH);
182             mcxnxxx_udelay(100);
183             rt_pin_write(cfg->scl, PIN_LOW);
184             mcxnxxx_udelay(100);
185         }
186     }
187     if (PIN_LOW == rt_pin_read(cfg->sda))
188     {
189         return -RT_ERROR;
190     }
191 
192     return RT_EOK;
193 }
194 
195 /**
196   * @brief  I2C initialization function
197   * @param  None
198   * @retval RT_EOK indicates successful initialization.
199   */
rt_hw_soft_i2c_init(void)200 int rt_hw_soft_i2c_init(void)
201 {
202     rt_size_t obj_num = sizeof(i2c_obj) / sizeof(struct mcxnxxx_i2c);
203     rt_err_t result;
204 
205     for (int i = 0; i < obj_num; i++)
206     {
207         i2c_obj[i].ops = mcxnxxx_bit_ops_default;
208         i2c_obj[i].ops.data = (void*)&soft_i2c_config[i];
209         i2c_obj[i].i2c2_bus.priv = &i2c_obj[i].ops;
210         mcxnxxx_i2c_gpio_init(&i2c_obj[i]);
211 
212         result = rt_i2c_bit_add_bus(&i2c_obj[i].i2c2_bus, soft_i2c_config[i].bus_name);
213 
214         RT_ASSERT(result == RT_EOK);
215 
216         mcxnxxx_i2c_bus_unlock(&soft_i2c_config[i]);
217 
218         LOG_D("software simulation %s init done, pin scl: %d, pin sda %d",
219         soft_i2c_config[i].bus_name,
220         soft_i2c_config[i].scl,
221         soft_i2c_config[i].sda);
222     }
223 
224     return RT_EOK;
225 }
226 INIT_APP_EXPORT(rt_hw_soft_i2c_init);
227 
228 #endif /* RT_USING_I2C */
229