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