1 /*
2 * Copyright (c) 2022 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_linv2_drv.h"
9
10 #define HPM_LIN_DRV_RETRY_COUNT (50000U)
11
lin_master_configure_timing(LINV2_Type * ptr,lin_timing_t * timing)12 hpm_stat_t lin_master_configure_timing(LINV2_Type *ptr, lin_timing_t *timing)
13 {
14 assert(timing->src_freq_in_hz >= 8000000U);
15 assert((timing->baudrate >= 1000U) && (timing->baudrate <= 20000U));
16
17 uint8_t prescaler, bt_mul;
18 uint16_t bt_div;
19
20 /** set master mode */
21 ptr->TIMING_CONTROL = LINV2_TIMING_CONTROL_MASTER_MODE_MASK;
22 ptr->TIMING_CONTROL |= LINV2_TIMING_CONTROL_LIN_INITIAL_MASK;
23 ptr->TIMING_CONTROL &= ~LINV2_TIMING_CONTROL_LIN_INITIAL_MASK;
24
25 bt_mul = 20000U / timing->baudrate - 1U;
26 prescaler = log((timing->src_freq_in_hz / ((bt_mul + 1U) * timing->baudrate * 200U))) / log(2U) - 1U;
27 bt_div = timing->src_freq_in_hz / ((1U << (prescaler + 1U)) * (bt_mul + 1U) * timing->baudrate);
28
29 if ((bt_div < 200) || (bt_div > 512)) {
30 return status_invalid_argument;
31 }
32
33 /** src =20MHz baudrate = 19.2KHz */
34 /** bt_div = 260, scaler = 1, bt_mul = 0 */
35 ptr->TIMING_CONTROL |= LINV2_TIMING_CONTROL_BT_DIV_SET(bt_div)
36 | LINV2_TIMING_CONTROL_BT_MUL_SET(bt_mul)
37 | LINV2_TIMING_CONTROL_PRESCL_SET(prescaler);
38
39 return status_success;
40 }
41
lin_slave_configure_timing(LINV2_Type * ptr,uint32_t src_freq_in_hz)42 hpm_stat_t lin_slave_configure_timing(LINV2_Type *ptr, uint32_t src_freq_in_hz)
43 {
44 assert(src_freq_in_hz >= 8000000U);
45
46 uint8_t prescaler;
47 uint16_t bt_div;
48
49 /** set slave mode, clean bt_div, bit_mul, prescl */
50 ptr->TIMING_CONTROL = 0;
51 ptr->TIMING_CONTROL |= LINV2_TIMING_CONTROL_LIN_INITIAL_MASK;
52 ptr->TIMING_CONTROL &= ~LINV2_TIMING_CONTROL_LIN_INITIAL_MASK;
53
54 prescaler = log((src_freq_in_hz / (20000U * 200U))) / log(2U) - 1U;
55 bt_div = src_freq_in_hz / ((1U << (prescaler + 1U)) * 20000U);
56
57 if ((bt_div < 200) || (bt_div >= 512)) {
58 return status_invalid_argument;
59 }
60
61 /** src = 20MHz, prescaler = 1, bt_div = 250 */
62 /* TODO: set wakeup_len */
63 ptr->TIMING_CONTROL = LINV2_TIMING_CONTROL_BT_DIV_SET(bt_div)
64 | LINV2_TIMING_CONTROL_PRESCL_SET(prescaler);
65
66 /* disable break_err detect */
67 ptr->CONTROL_STATUS = LINV2_CONTROL_STATUS_BREAK_ERR_DIS_MASK;
68
69 return status_success;
70 }
71
lin_get_data_length_from_id(uint8_t id)72 uint8_t lin_get_data_length_from_id(uint8_t id)
73 {
74 switch (LIN_ID_DATA_LEN_GET(id)) {
75 case id_data_length_2bytes:
76 return 2;
77 case id_data_length_2bytes_2:
78 return 2;
79 case id_data_length_4bytes:
80 return 4;
81 case id_data_length_8bytes:
82 return 8;
83 default:
84 return 8;
85 }
86 }
87
lin_get_data_length(LINV2_Type * ptr)88 uint8_t lin_get_data_length(LINV2_Type *ptr)
89 {
90 uint8_t data_length = 0;
91 if (((ptr->DATA_LEN_ID) & LINV2_DATA_LEN_ID_DATA_LEN_MASK) == LINV2_DATA_LEN_ID_DATA_LEN_MASK) {
92 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
93 } else {
94 data_length = LINV2_DATA_LEN_ID_DATA_LEN_GET(ptr->DATA_LEN_ID);
95 }
96 return data_length;
97 }
98
lin_master_transfer(LINV2_Type * ptr,lin_trans_config_t * config)99 void lin_master_transfer(LINV2_Type *ptr, lin_trans_config_t *config)
100 {
101 uint8_t data_length;
102
103 /** config id */
104 if (config->data_length_from_id) {
105 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
106 ptr->DATA_LEN_ID = LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_MASK | LINV2_DATA_LEN_ID_ID_SET(config->id);
107 } else {
108 data_length = config->data_length;
109 ptr->DATA_LEN_ID = LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_SET(data_length) | LINV2_DATA_LEN_ID_ID_SET(config->id);
110 }
111
112 /** sent or receive */
113 ptr->CONTROL_STATUS = 0U;
114 if (config->transmit) {
115 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_TRANSMIT_MASK;
116 }
117
118 if (config->transmit) {
119 for (uint8_t i = 0; i < data_length; i++) {
120 ptr->DATA_BYTE[i] = *((config->data_buff)++);
121 }
122 }
123
124 /** start */
125 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_START_REQ_MASK;
126 }
127
lin_master_sent(LINV2_Type * ptr,lin_trans_config_t * config)128 hpm_stat_t lin_master_sent(LINV2_Type *ptr, lin_trans_config_t *config)
129 {
130 uint32_t retry = 0;
131 uint8_t data_length = 0;
132
133 /** wait for lin inactive */
134 while (lin_is_active(ptr)) {
135 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
136 break;
137 }
138 retry++;
139 }
140
141 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
142 return status_timeout;
143 }
144
145 /** config id */
146 if (config->data_length_from_id) {
147 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
148 ptr->DATA_LEN_ID = LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_MASK | LINV2_DATA_LEN_ID_ID_SET(config->id);
149 } else {
150 data_length = config->data_length;
151 ptr->DATA_LEN_ID = LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_SET(data_length) | LINV2_DATA_LEN_ID_ID_SET(config->id);
152 }
153
154 ptr->CONTROL_STATUS = LINV2_CONTROL_STATUS_TRANSMIT_MASK;
155
156 /** load data into registers */
157 for (uint8_t i = 0; i < data_length; i++) {
158 ptr->DATA_BYTE[i] = *((config->data_buff)++);
159 }
160
161 /** start */
162 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_START_REQ_MASK;
163
164 /** waiting for lin complete */
165 retry = 0;
166 while (!lin_is_complete(ptr)) {
167 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
168 break;
169 }
170 retry++;
171 }
172
173 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
174 return status_timeout;
175 }
176 return status_success;
177 }
178
lin_master_receive(LINV2_Type * ptr,lin_trans_config_t * config)179 hpm_stat_t lin_master_receive(LINV2_Type *ptr, lin_trans_config_t *config)
180 {
181 uint32_t retry = 0;
182 uint8_t data_length;
183
184 /** waiting for lin inactive */
185 while (lin_is_active(ptr)) {
186 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
187 break;
188 }
189 retry++;
190 }
191
192 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
193 return status_timeout;
194 }
195
196 /** config id */
197 if (config->data_length_from_id) {
198 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
199 ptr->DATA_LEN_ID = LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_MASK | LINV2_DATA_LEN_ID_ID_SET(config->id);
200 } else {
201 data_length = config->data_length;
202 ptr->DATA_LEN_ID = LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_SET(data_length) | LINV2_DATA_LEN_ID_ID_SET(config->id);
203 }
204
205 /** receive */
206 ptr->CONTROL_STATUS = 0U;
207 /** start */
208 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_START_REQ_MASK;
209
210 /** waiting for receive complete */
211 retry = 0;
212 while (!lin_is_complete(ptr)) {
213 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
214 break;
215 }
216 retry++;
217 }
218
219 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
220 return status_fail;
221 }
222
223 /** load register data into buffer */
224 for (uint8_t i = 0; i < data_length; i++) {
225 *((config->data_buff)++) = ptr->DATA_BYTE[i];
226 }
227
228 return status_success;
229 }
230
lin_slave_transfer(LINV2_Type * ptr,lin_trans_config_t * config)231 void lin_slave_transfer(LINV2_Type *ptr, lin_trans_config_t *config)
232 {
233 uint8_t data_length;
234
235 /** transmit or receive */
236 ptr->CONTROL_STATUS &= ~LINV2_CONTROL_STATUS_TRANSMIT_MASK;
237 if (config->transmit) {
238 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_TRANSMIT_MASK;
239 }
240
241 /* clean enh_check and data_len */
242 ptr->DATA_LEN_ID &= ~(LINV2_DATA_LEN_ID_ENH_CHECK_MASK | LINV2_DATA_LEN_ID_DATA_LEN_MASK);
243 if (config->data_length_from_id) {
244 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
245 ptr->DATA_LEN_ID |= LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_MASK;
246 } else {
247 data_length = config->data_length;
248 ptr->DATA_LEN_ID |= LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_SET(data_length);
249 }
250
251 if (config->transmit) {
252 for (uint8_t i = 0; i < data_length; i++) {
253 ptr->DATA_BYTE[i] = *((config->data_buff)++);
254 }
255 }
256
257 /** data ack */
258 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_DATA_ACK_MASK;
259 }
260
lin_slave_sent(LINV2_Type * ptr,lin_trans_config_t * config)261 hpm_stat_t lin_slave_sent(LINV2_Type *ptr, lin_trans_config_t *config)
262 {
263 uint32_t retry = 0;
264 uint8_t data_length;
265
266 /** waiting for lin data_req */
267 while (!((ptr->CONTROL_STATUS & LINV2_CONTROL_STATUS_DATA_REQ_MASK) == LINV2_CONTROL_STATUS_DATA_REQ_MASK)) {
268 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
269 break;
270 }
271 retry++;
272 }
273
274 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
275 return status_timeout;
276 }
277
278 /** transmit */
279 ptr->CONTROL_STATUS = LINV2_CONTROL_STATUS_TRANSMIT_MASK;
280
281 /* clean enh_check and data_len */
282 ptr->DATA_LEN_ID &= ~(LINV2_DATA_LEN_ID_ENH_CHECK_MASK | LINV2_DATA_LEN_ID_DATA_LEN_MASK);
283 if (config->data_length_from_id) {
284 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
285 ptr->DATA_LEN_ID |= LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_MASK;
286 } else {
287 data_length = config->data_length;
288 ptr->DATA_LEN_ID |= LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_SET(data_length);
289 }
290
291 for (uint8_t i = 0; i < data_length; i++) {
292 ptr->DATA_BYTE[i] = *((config->data_buff)++);
293 }
294
295 /** data ack */
296 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_DATA_ACK_MASK;
297
298 /** waiting for lin complete */
299 retry = 0;
300 while (!lin_is_complete(ptr)) {
301 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
302 break;
303 }
304 retry++;
305 }
306
307 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
308 return status_timeout;
309 }
310 return status_success;
311 }
312
lin_slave_receive(LINV2_Type * ptr,lin_trans_config_t * config)313 hpm_stat_t lin_slave_receive(LINV2_Type *ptr, lin_trans_config_t *config)
314 {
315 uint32_t retry = 0;
316 uint8_t data_length;
317
318 /** waiting for lin data_req */
319 while (!((ptr->CONTROL_STATUS & LINV2_CONTROL_STATUS_DATA_REQ_MASK) == LINV2_CONTROL_STATUS_DATA_REQ_MASK)) {
320 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
321 break;
322 }
323 retry++;
324 }
325
326 if (retry > HPM_LIN_DRV_RETRY_COUNT) {
327 return status_timeout;
328 }
329
330 /** receive */
331 ptr->CONTROL_STATUS = 0U;
332
333 /* clean enh_check and data_len */
334 ptr->DATA_LEN_ID &= ~(LINV2_DATA_LEN_ID_ENH_CHECK_MASK | LINV2_DATA_LEN_ID_DATA_LEN_MASK);
335 if (config->data_length_from_id) {
336 data_length = lin_get_data_length_from_id(lin_get_id(ptr));
337 ptr->DATA_LEN_ID |= LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_MASK;
338 } else {
339 data_length = config->data_length;
340 ptr->DATA_LEN_ID |= LINV2_DATA_LEN_ID_ENH_CHECK_SET(config->enhanced_checksum) | LINV2_DATA_LEN_ID_DATA_LEN_SET(data_length);
341 }
342
343 /** data ack */
344 ptr->CONTROL_STATUS |= LINV2_CONTROL_STATUS_DATA_ACK_MASK;
345
346 /** waiting for lin complete */
347 retry = 0;
348 while (!lin_is_complete(ptr)) {
349 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
350 break;
351 }
352 retry++;
353 }
354
355 if (retry > HPM_LIN_DRV_RETRY_COUNT * 8) {
356 return status_timeout;
357 }
358
359 for (uint8_t i = 0; i < data_length; i++) {
360 *((config->data_buff)++) = ptr->DATA_BYTE[i];
361 }
362
363 return status_success;
364 }
365
lin_slave_dma_transfer(LINV2_Type * ptr,lin_trans_config_t * config)366 void lin_slave_dma_transfer(LINV2_Type *ptr, lin_trans_config_t *config)
367 {
368 ptr->DMA_CONTROL = LINV2_DMA_CONTROL_DMA_REQ_ENABLE_MASK
369 | LINV2_DMA_CONTROL_DMA_REQ_ID_SET(config->id)
370 | LINV2_DMA_CONTROL_DMA_REQ_ID_TYPE_SET(config->transmit)
371 | LINV2_DMA_CONTROL_DMA_REQ_LEN_SET(config->data_length)
372 | LINV2_DMA_CONTROL_DMA_REQ_ENH_CHK_SET(config->enhanced_checksum);
373 }