1 /*
2 * Copyright 2021 QuickLogic
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * SPDX-License-Identifier: Apache-2.0
17 */
18 #include "rtconfig.h"
19 #ifdef PKG_USING_FREERTOS_WRAPPER
20 #include "FreeRTOS.h"
21 #include <string.h>
22 #include <stdint.h>
23 #include <stdbool.h>
24 #include "semphr.h"
25 #include "core-v-mcu-config.h"
26 #include "hal_fc_event.h"
27 #include "hal_udma_ctrl_reg_defs.h"
28 #include "hal_udma_i2cm_reg_defs.h"
29 #include <udma_i2cm_driver.h>
30
31 SemaphoreHandle_t i2cm_semaphores_rx[N_I2CM];
32 SemaphoreHandle_t i2cm_semaphores_tx[N_I2CM];
33
i2cmTXHandler(void * arg)34 void i2cmTXHandler(void *arg)
35 {
36 uint32_t lCounter = 0;
37
38 lCounter++;
39 }
40
i2cmRXHandler(void * arg)41 void i2cmRXHandler(void *arg)
42 {
43 uint32_t lCounter = 0;
44
45 lCounter++;
46 }
47
48 static uint8_t aucclkdiv[2];
49
udma_i2cm_open(uint8_t i2cm_id,uint32_t clk_freq)50 uint16_t udma_i2cm_open (uint8_t i2cm_id, uint32_t clk_freq) {
51 volatile UdmaCtrl_t* pudma_ctrl = (UdmaCtrl_t*)UDMA_CH_ADDR_CTRL;
52 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
53 uint32_t clk_divisor;
54
55 /* See if already initialized */
56 if (i2cm_semaphores_rx[i2cm_id] != NULL || i2cm_semaphores_tx[i2cm_id] != NULL) {
57 return 1;
58 }
59 /* Enable reset and enable uart clock */
60 pudma_ctrl->reg_rst |= (UDMA_CTRL_I2CM0_CLKEN << i2cm_id);
61 pudma_ctrl->reg_rst &= ~(UDMA_CTRL_I2CM0_CLKEN << i2cm_id);
62 pudma_ctrl->reg_cg |= (UDMA_CTRL_I2CM0_CLKEN << i2cm_id);
63
64 /* Set semaphore */
65 SemaphoreHandle_t shSemaphoreHandle; // FreeRTOS.h has a define for xSemaphoreHandle, so can't use that
66 shSemaphoreHandle = xSemaphoreCreateBinary();
67 configASSERT(shSemaphoreHandle);
68 xSemaphoreGive(shSemaphoreHandle);
69 i2cm_semaphores_rx[i2cm_id] = shSemaphoreHandle;
70
71 shSemaphoreHandle = xSemaphoreCreateBinary();
72 configASSERT(shSemaphoreHandle);
73 xSemaphoreGive(shSemaphoreHandle);
74 i2cm_semaphores_tx[i2cm_id] = shSemaphoreHandle;
75
76 /* Set handlers. */
77 pi_fc_event_handler_set(SOC_EVENT_UDMA_I2C_RX(i2cm_id), i2cmRXHandler/*NULL*/, i2cm_semaphores_rx[i2cm_id]);
78 pi_fc_event_handler_set(SOC_EVENT_UDMA_I2C_TX(i2cm_id), i2cmTXHandler/*NULL*/, i2cm_semaphores_tx[i2cm_id]);
79 /* Enable SOC events propagation to FC. */
80 hal_soc_eu_set_fc_mask(SOC_EVENT_UDMA_I2C_RX(i2cm_id));
81 hal_soc_eu_set_fc_mask(SOC_EVENT_UDMA_I2C_TX(i2cm_id));
82
83 /* configure */
84 clk_divisor = 5000000/clk_freq;
85 aucclkdiv[0] = (clk_divisor >> 0) & 0xFF;
86 aucclkdiv[1] = (clk_divisor >> 8) & 0xFF;
87
88 return 0;
89 }
90
udma_i2cm_control(uint8_t i2cm_id,udma_i2cm_control_type_t control_type,void * pparam)91 uint16_t udma_i2cm_control(uint8_t i2cm_id, udma_i2cm_control_type_t control_type, void* pparam) {
92 volatile UdmaCtrl_t* pudma_ctrl = (UdmaCtrl_t*)UDMA_CH_ADDR_CTRL;
93 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
94
95 switch(control_type) {
96 case kI2cmReset:
97 pudma_ctrl->reg_rst |= (UDMA_CTRL_I2CM0_CLKEN << i2cm_id);
98 pudma_ctrl->reg_rst &= ~(UDMA_CTRL_I2CM0_CLKEN << i2cm_id);
99 break;
100 default:
101 configASSERT(0);
102 }
103 return 0;
104 }
105
106 static uint8_t auccmd_rx[16];
107
udma_i2cm_read(uint8_t i2cm_id,uint8_t i2cm_addr,uint8_t reg_addr,uint16_t read_len,uint8_t * read_buffer,bool more_follows)108 uint8_t udma_i2cm_read(uint8_t i2cm_id, uint8_t i2cm_addr, uint8_t reg_addr, uint16_t read_len, uint8_t* read_buffer, bool more_follows) {
109 _udma_i2cm_write_addr_plus_regaddr(i2cm_id, i2cm_addr, reg_addr);
110 return _udma_i2cm_read(i2cm_id, i2cm_addr, read_len, read_buffer, more_follows);
111 }
udma_i2cm_16read8(uint8_t i2cm_id,uint8_t i2cm_addr,uint16_t reg_addr,uint16_t read_len,uint8_t * read_buffer,bool more_follows)112 uint8_t udma_i2cm_16read8(uint8_t i2cm_id, uint8_t i2cm_addr, uint16_t reg_addr, uint16_t read_len, uint8_t* read_buffer, bool more_follows) {
113 _udma_i2cm_write_addr_plus_reg16addr(i2cm_id, i2cm_addr, reg_addr);
114 return _udma_i2cm_read(i2cm_id, i2cm_addr, read_len, read_buffer, more_follows);
115 }
116
117 static uint8_t auccmd_tx[32];
udma_i2cm_write(uint8_t i2cm_id,uint8_t i2cm_addr,uint8_t reg_addr,uint16_t write_len,uint8_t * write_data,bool more_follows)118 uint8_t udma_i2cm_write (uint8_t i2cm_id, uint8_t i2cm_addr, uint8_t reg_addr, uint16_t write_len, uint8_t *write_data, bool more_follows) {
119 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
120 uint8_t* pcmd = auccmd_tx;
121 uint8_t* pdata = write_data;
122 SemaphoreHandle_t shSemaphoreHandleTx = i2cm_semaphores_tx[i2cm_id];
123 uint8_t lStatus = pdFALSE;
124
125 configASSERT(write_len < 256);
126
127 if( xSemaphoreTake( shSemaphoreHandleTx, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE ) // Wait for any prior transmission to complete
128 {
129 *pcmd++ = kI2cmCmdCfg;
130 *pcmd++ = aucclkdiv[1];
131 *pcmd++ = aucclkdiv[0];
132 *pcmd++ = kI2cmCmdStart; // Put Start transaction on I2C bus
133 *pcmd++ = kI2cmCmdRpt; // Set up for several writes: i2cm_CMD_RPT
134 *pcmd++ = (uint8_t)(write_len + 2); // I@CM_ADDR + REG_ADDR + data
135 *pcmd++ = kI2cmCmdWr; // Command to repeat: I2C CMD_WR
136 *pcmd++ = i2cm_addr & 0xfe; // Clear R/WRbar bit from i2c device's address to indicate write
137 *pcmd++ = reg_addr; // Target address for following data
138 for (int i = 0; i != write_len; i++) {
139 *pcmd++ = *pdata++;
140 }
141 pi2cm_regs->tx_saddr = auccmd_tx;
142 pi2cm_regs->tx_size = (uint32_t)(pcmd - auccmd_tx);
143 pi2cm_regs->tx_cfg_b.en = 1;
144
145 // Block until UDMA transaction is completed
146 xSemaphoreTake( shSemaphoreHandleTx, SEMAPHORE_WAIT_TIME_IN_MS );
147 xSemaphoreGive( shSemaphoreHandleTx );
148
149 if (!more_follows) {
150 _udma_i2cm_send_stop(i2cm_id);
151 }
152 lStatus = pdTRUE;
153 }
154 else
155 {
156 xSemaphoreGive( shSemaphoreHandleTx );
157 }
158 return lStatus;
159 }
160
_udma_i2cm_write_addr_plus_regaddr(uint8_t i2cm_id,uint8_t i2cm_addr,uint8_t reg_addr)161 uint8_t _udma_i2cm_write_addr_plus_regaddr (uint8_t i2cm_id, uint8_t i2cm_addr, uint8_t reg_addr) {
162 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
163 uint8_t* pcmd = auccmd_tx;
164 uint8_t lStatus = pdFALSE;
165
166 SemaphoreHandle_t shSemaphoreHandle = i2cm_semaphores_tx[i2cm_id];
167 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
168 {
169 pi2cm_regs->tx_cfg_b.en = 0;
170 *pcmd++ = kI2cmCmdCfg;
171 *pcmd++ = aucclkdiv[1];
172 *pcmd++ = aucclkdiv[0];
173 *pcmd++ = kI2cmCmdStart; // Put Start transaction on I2C bus
174 *pcmd++ = kI2cmCmdWr; // Write device's address (next byte)
175 *pcmd++ = i2cm_addr & 0xfe; // Clear R/WRbar bit from i2c device's address to indicate write
176 *pcmd++ = kI2cmCmdWr; // I2C CMD_WR
177 pi2cm_regs->tx_saddr = auccmd_tx;
178 pi2cm_regs->tx_size = (uint32_t)(pcmd - auccmd_tx);
179 pi2cm_regs->tx_cfg_b.en = 1;
180
181 // Block until UDMA operation is completed
182 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
183 {
184 //pi2cm_regs->tx_cfg_b.en = 0;
185 pcmd = auccmd_tx;
186 *pcmd++ = reg_addr;
187 pi2cm_regs->tx_saddr = auccmd_tx;
188 pi2cm_regs->tx_size = (uint32_t)(pcmd - auccmd_tx);
189 pi2cm_regs->tx_cfg_b.en = 1;
190
191 // Block until UDMA operation is completed
192 xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS );
193 xSemaphoreGive( shSemaphoreHandle );
194 lStatus = pdTRUE;
195 }
196 else
197 {
198 xSemaphoreGive( shSemaphoreHandle );
199 }
200 }
201 else
202 {
203 xSemaphoreGive( shSemaphoreHandle );
204 }
205 return lStatus;
206 }
_udma_i2cm_write_addr_plus_reg16addr(uint8_t i2cm_id,uint8_t i2cm_addr,uint16_t reg_addr)207 uint8_t _udma_i2cm_write_addr_plus_reg16addr (uint8_t i2cm_id, uint8_t i2cm_addr, uint16_t reg_addr) {
208 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
209 uint8_t* pcmd = auccmd_tx;
210 uint8_t lStatus = pdFALSE;
211
212 SemaphoreHandle_t shSemaphoreHandle = i2cm_semaphores_tx[i2cm_id];
213 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
214 {
215 pi2cm_regs->tx_cfg_b.en = 0;
216 *pcmd++ = kI2cmCmdCfg;
217 *pcmd++ = aucclkdiv[1];
218 *pcmd++ = aucclkdiv[0];
219 *pcmd++ = kI2cmCmdStart; // Put Start transaction on I2C bus
220 *pcmd++ = kI2cmCmdWr; // Write device's address (next byte)
221 *pcmd++ = i2cm_addr & 0xfe; // Clear R/WRbar bit from i2c device's address to indicate write
222 *pcmd++ = kI2cmCmdRpt; // 2 byte register address
223 *pcmd++ = 2;
224 *pcmd++ = kI2cmCmdWr; // I2C CMD_WR
225 pi2cm_regs->tx_saddr = auccmd_tx;
226 pi2cm_regs->tx_size = (uint32_t)(pcmd - auccmd_tx);
227 pi2cm_regs->tx_cfg_b.en = 1;
228
229 // Block until UDMA operation is completed
230 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
231 {
232 //pi2cm_regs->tx_cfg_b.en = 0;
233 pcmd = auccmd_tx;
234 *pcmd++ = reg_addr & 0xff;
235 *pcmd++ = (reg_addr >> 8) & 0xff;
236 pi2cm_regs->tx_saddr = auccmd_tx;
237 pi2cm_regs->tx_size = (uint32_t)(pcmd - auccmd_tx);
238 pi2cm_regs->tx_cfg_b.en = 1;
239
240 // Block until UDMA operation is completed
241 xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS );
242 xSemaphoreGive( shSemaphoreHandle );
243
244 lStatus = pdTRUE;
245 }
246 else
247 {
248 xSemaphoreGive( shSemaphoreHandle );
249 }
250 }
251 else
252 {
253 xSemaphoreGive( shSemaphoreHandle );
254 }
255 return lStatus;
256 }
257
258
_udma_i2cm_read(uint8_t i2cm_id,uint8_t i2cm_addr,uint16_t read_len,uint8_t * read_buffer,bool more_follows)259 uint8_t _udma_i2cm_read(uint8_t i2cm_id, uint8_t i2cm_addr, uint16_t read_len, uint8_t* read_buffer, bool more_follows) {
260 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
261 uint8_t* pcmd = auccmd_rx;
262 uint8_t lStatus = pdFALSE;
263
264 configASSERT(read_len < 256);
265
266 SemaphoreHandle_t shSemaphoreHandle = i2cm_semaphores_rx[i2cm_id];
267 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
268 {
269 shSemaphoreHandle = i2cm_semaphores_tx[i2cm_id];
270 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
271 {
272 pi2cm_regs->tx_cfg_b.en = 0;
273 *pcmd++ = kI2cmCmdCfg;
274 *pcmd++ = aucclkdiv[1];
275 *pcmd++ = aucclkdiv[0];
276 *pcmd++ = kI2cmCmdStart; // Put Start transaction on I2C bus
277 *pcmd++ = kI2cmCmdWr; // Write device's address (next byte)
278 *pcmd++ = i2cm_addr | 0x01; // Device's address with read bit set
279 if (read_len > 1) { // Do len-1 reads with ACK, and follow by 1 read with NACK
280 *pcmd++ = kI2cmCmdRpt; // Tell controller to repeat the following command
281 *pcmd++ = (uint8_t)(read_len - 1); // len-1 times
282 *pcmd++ = kI2cmCmdRdAck; // command to repeat is read with ack
283 }
284 *pcmd++ = kI2cmCmdRdNack; // Read last byte with NACK to indicate the end of the read
285
286 //
287 pi2cm_regs->rx_saddr = read_buffer;
288 pi2cm_regs->rx_size = read_len;
289 pi2cm_regs->rx_cfg_b.en = 1;
290
291 pi2cm_regs->tx_saddr = auccmd_rx;
292 pi2cm_regs->tx_size = (uint32_t)(pcmd - auccmd_rx);
293 pi2cm_regs->tx_cfg_b.en = 1;
294
295 // Block until UDMA operation is complete
296 shSemaphoreHandle = i2cm_semaphores_rx[i2cm_id];
297 xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS );
298 xSemaphoreGive( shSemaphoreHandle );
299
300 shSemaphoreHandle = i2cm_semaphores_tx[i2cm_id];
301
302 xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS );
303 xSemaphoreGive( shSemaphoreHandle );
304
305 if (!more_follows) {
306 _udma_i2cm_send_stop(i2cm_id);
307 }
308 lStatus = pdTRUE;
309 }
310 else
311 {
312 xSemaphoreGive( shSemaphoreHandle );
313 lStatus = pdFALSE;
314 }
315 }
316 else
317 {
318 xSemaphoreGive( shSemaphoreHandle );
319 lStatus = pdFALSE;
320 }
321 return lStatus;
322 }
323
324 static uint8_t auci2cm_stop_seq[] = {
325 kI2cmCmdStop, kI2cmCmdWait, 0x0
326 };
327
_udma_i2cm_send_stop(uint8_t i2cm_id)328 uint8_t _udma_i2cm_send_stop(uint8_t i2cm_id) {
329 UdmaI2cm_t* pi2cm_regs = (UdmaI2cm_t*)(UDMA_CH_ADDR_I2CM + i2cm_id * UDMA_CH_SIZE);
330 SemaphoreHandle_t shSemaphoreHandle = i2cm_semaphores_tx[i2cm_id];
331 uint8_t lStatus = pdFALSE;
332
333 if( xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS ) == pdTRUE )
334 {
335 pi2cm_regs->tx_saddr = auci2cm_stop_seq;
336 pi2cm_regs->tx_size = sizeof(auci2cm_stop_seq);
337 pi2cm_regs->tx_cfg_b.en = 1;
338
339 // Block until UDMA transaction is completed
340 xSemaphoreTake( shSemaphoreHandle, SEMAPHORE_WAIT_TIME_IN_MS );
341 xSemaphoreGive( shSemaphoreHandle );
342 lStatus = pdTRUE;
343 }
344 else
345 {
346 xSemaphoreGive( shSemaphoreHandle );
347 }
348 return lStatus;
349 }
350 #endif
351