1 /*
2  * Copyright (c) 2023 HPMicro
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "hpm_uart_lin.h"
9 
10 #ifndef HPM_UART_LIN_RETRY_COUNT
11 #define HPM_UART_LIN_RETRY_COUNT (50000U)
12 #endif
13 
14 #ifndef HPM_UART_LIN_BREAK_LENGTH
15 #define HPM_UART_LIN_BREAK_LENGTH (13U)  /* bits */
16 #endif
17 
18 #ifndef HPM_UART_LIN_WAKEUP_LENGTH
19 #define HPM_UART_LIN_WAKEUP_LENGTH (400U) /* us */
20 #endif
21 
22 
hpm_uart_lin_calculate_protected_id(uint8_t id)23 uint8_t hpm_uart_lin_calculate_protected_id(uint8_t id)
24 {
25     uint8_t id0, id1, id2, id3, id4, id5, p0, p1, pid;
26 
27     /* P0 = ID0 @ ID1 @ ID2 @ ID3 @ ID4 */
28     /* P1 = !(ID1 @ ID2 @ ID3 @ ID4 @ ID5) */
29     id0 = (id >> 0U) & 0x1U;
30     id1 = (id >> 1U) & 0x1U;
31     id2 = (id >> 2U) & 0x1U;
32     id3 = (id >> 3U) & 0x1U;
33     id4 = (id >> 4U) & 0x1U;
34     id5 = (id >> 5U) & 0x1U;
35 
36     p0 = id0 ^ id1 ^ id2 ^ id4;
37     p1 = !(id1 ^ id3 ^ id4 ^ id5);
38     pid  = (p1 << 7) | (p0 << 6) | id;
39     return pid;
40 }
41 
hpm_uart_lin_calculate_checksum(uint8_t id,uint8_t * data,uint8_t length,bool enhanced_checksum)42 static uint8_t hpm_uart_lin_calculate_checksum(uint8_t id, uint8_t *data, uint8_t length, bool enhanced_checksum)
43 {
44     assert(length <= 8U);
45     uint8_t checksum = 0;
46     uint16_t temp;
47     for (uint8_t i = 0; i < length; i++) {
48         temp = checksum + data[i];
49         checksum += data[i] + (temp >> 8U);
50     }
51 
52     if (enhanced_checksum) {
53         temp = checksum + id;
54         checksum += id + (temp >> 8U);
55     }
56 
57     checksum = ~checksum;
58     return checksum;
59 }
60 
hpm_uart_lin_check_checksum(uint8_t id,uint8_t * data,uint8_t length,bool enhanced_checksum,uint8_t checksum)61 static bool hpm_uart_lin_check_checksum(uint8_t id, uint8_t *data, uint8_t length, bool enhanced_checksum, uint8_t checksum)
62 {
63     uint8_t cal_checksum;
64     cal_checksum = hpm_uart_lin_calculate_checksum(id, data, length, enhanced_checksum);
65 
66     if (cal_checksum != checksum) {
67         return false;
68     }
69     return true;
70 }
71 
72 
hpm_uart_lin_send_break(UART_Type * ptr,uart_lin_master_pin_ctrl_t * pin_ctrl)73 static void hpm_uart_lin_send_break(UART_Type *ptr, uart_lin_master_pin_ctrl_t *pin_ctrl)
74 {
75     assert(pin_ctrl->baudrate <= 20000);
76 
77     uint32_t bit_period_us = 1000000 / pin_ctrl->baudrate;
78     uint32_t break_period_us = bit_period_us * HPM_UART_LIN_BREAK_LENGTH;
79     pin_ctrl->config_uart_pin_as_gpio(ptr);
80     gpio_set_pin_output(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin);
81     gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 0);
82     pin_ctrl->delay_us(break_period_us);
83     gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 1);
84     pin_ctrl->delay_us(bit_period_us);
85     pin_ctrl->config_uart_pin(ptr);
86 }
87 
hpm_uart_lin_send_sync(UART_Type * ptr)88 static void hpm_uart_lin_send_sync(UART_Type *ptr)
89 {
90     uart_write_byte(ptr, 0x55); /* sync phase */
91 }
92 
hpm_uart_lin_send_wakeup(UART_Type * ptr,uart_lin_master_pin_ctrl_t * pin_ctrl)93 void hpm_uart_lin_send_wakeup(UART_Type *ptr, uart_lin_master_pin_ctrl_t *pin_ctrl)
94 {
95     pin_ctrl->config_uart_pin_as_gpio(ptr);
96     gpio_set_pin_output(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin);
97     gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 0);
98     pin_ctrl->delay_us(HPM_UART_LIN_WAKEUP_LENGTH);
99     gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 1);
100     pin_ctrl->config_uart_pin(ptr);
101 }
102 
hpm_uart_lin_master_send_frame(uart_lin_master_config_t * config)103 uart_lin_stat_t hpm_uart_lin_master_send_frame(uart_lin_master_config_t *config)
104 {
105     uint32_t retry;
106     UART_Type *ptr = config->ptr;
107     uart_lin_data_t data = config->data;
108     uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
109     uint8_t checksum = hpm_uart_lin_calculate_checksum(pid, data.buff, data.length, data.enhance_checksum);
110     uint8_t send_data[11] = {0}; /* max 8 data bytes + 1 byte 0x55 + 1 byte pid + 1byte checksum */
111     uint8_t length = data.length + 3;
112 
113     assert(data.length > 0);
114 
115     /* 0x55 - pid  - data - checksum */
116     send_data[0] = 0x55;
117     send_data[1] = pid;
118     memcpy(&send_data[2], data.buff, data.length);
119     send_data[data.length + 2] = checksum;
120 
121     hpm_uart_lin_send_break(ptr, &(config->pin_ctrl));
122 
123     for (uint8_t i = 0; i < length; i++) {
124         retry = 0;
125         while (!uart_check_status(ptr, uart_stat_tx_slot_avail)) {
126             if (retry > HPM_UART_LIN_RETRY_COUNT) {
127                 break;
128             }
129             retry++;
130         }
131 
132         if (retry > HPM_UART_LIN_RETRY_COUNT) {
133             return uart_lin_timeout;
134         }
135 
136         uart_write_byte(ptr, send_data[i]);
137     }
138 
139     return uart_lin_success;
140 }
141 
142 
hpm_uart_lin_master_receive_frame(uart_lin_master_config_t * config)143 uart_lin_stat_t hpm_uart_lin_master_receive_frame(uart_lin_master_config_t *config)
144 {
145     uint32_t retry = 0;
146     UART_Type *ptr = config->ptr;
147     uart_lin_data_t data = config->data;
148     uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
149     uint8_t checksum;
150     uint8_t *buff = data.buff;
151 
152     assert(data.length > 0);
153 
154     /* clear data in rx fifo */
155     uart_clear_rx_fifo(ptr);
156 
157     hpm_uart_lin_send_break(ptr, &(config->pin_ctrl));
158 
159     hpm_uart_lin_send_sync(ptr);
160 
161     uart_write_byte(ptr, pid);
162 
163     /* wait for send 0x55 and pid */
164     while (!uart_check_status(ptr, uart_stat_tx_slot_avail)) {
165         if (retry > HPM_UART_LIN_RETRY_COUNT * 2) {
166             break;
167         }
168         retry++;
169     }
170 
171     if (retry > HPM_UART_LIN_RETRY_COUNT * 2) {
172         return uart_lin_timeout;
173     }
174 
175     /* wait for receive complete */
176     for (uint8_t i = 0; i < data.length + 3; i++) {
177         retry = 0;
178         while (!uart_check_status(ptr, uart_stat_data_ready)) {
179             if (retry > HPM_UART_LIN_RETRY_COUNT) {
180                 break;
181             }
182             retry++;
183         }
184 
185         if (retry > HPM_UART_LIN_RETRY_COUNT) {
186             return uart_lin_timeout;
187         }
188 
189         if (i < 2) {
190             uart_read_byte(ptr);
191         } else if (i < data.length + 2) {
192             *(buff++) = uart_read_byte(ptr);
193         } else {
194             checksum = uart_read_byte(ptr);
195         }
196     }
197 
198     if (!hpm_uart_lin_check_checksum(pid, data.buff, data.length, data.enhance_checksum, checksum)) {
199         return uart_lin_checksum_error;
200     }
201 
202     return uart_lin_success;
203 }
204 
205 /* generate break with gpio then write 0x55 and pid into uart tx fifo */
hpm_uart_lin_master_send_head(uart_lin_master_config_t * config)206 void hpm_uart_lin_master_send_head(uart_lin_master_config_t *config)
207 {
208     UART_Type *ptr = config->ptr;
209     uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
210 
211     /* clear data in rx fifo */
212     uart_clear_rx_fifo(ptr);
213 
214     hpm_uart_lin_send_break(ptr, &(config->pin_ctrl));
215 
216     hpm_uart_lin_send_sync(ptr);
217 
218     uart_write_byte(ptr, pid);
219 }
220 
221 /* write data into uart tx fifo including data and checksum */
hpm_uart_lin_master_send_data(uart_lin_master_config_t * config)222 void hpm_uart_lin_master_send_data(uart_lin_master_config_t *config)
223 {
224     UART_Type *ptr = config->ptr;
225     uart_lin_data_t data = config->data;
226 
227     assert(data.length > 0);
228 
229     uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
230     uint8_t checksum = hpm_uart_lin_calculate_checksum(pid, data.buff, data.length, data.enhance_checksum);
231 
232     for (uint8_t i = 0; i < data.length; i++) {
233         uart_write_byte(ptr, *(data.buff++));
234     }
235 
236     uart_write_byte(ptr, checksum);
237 }
238 
239 /* call this function in rx timeout isr */
240 /* read data from uart rx fifo */
hpm_uart_lin_master_receive_data(uart_lin_master_config_t * config)241 uart_lin_stat_t hpm_uart_lin_master_receive_data(uart_lin_master_config_t *config)
242 {
243     UART_Type *ptr = config->ptr;
244     uart_lin_data_t data = config->data;
245     uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
246     uint8_t checksum = 0;
247     uint8_t index = 0;
248     uint8_t *buff = data.buff;
249 
250     assert(data.length > 0);
251 
252     while (uart_check_status(ptr, uart_stat_data_ready)) {
253         if (index >= data.length + 3) {
254             break;
255         }
256         if (index < 2) {
257             uart_read_byte(ptr); /* read 0x55 and pid */
258         } else if (index < data.length + 2) {
259             *(buff++) = uart_read_byte(ptr);
260         } else {
261             checksum = uart_read_byte(ptr);
262         }
263         index++;
264     }
265 
266     if (index != data.length + 3) {
267         return uart_lin_frame_error;
268     }
269 
270     if (!hpm_uart_lin_check_checksum(pid, data.buff, data.length, data.enhance_checksum, checksum)) {
271         return uart_lin_checksum_error;
272     }
273 
274     return uart_lin_success;
275 }
276 
277 /* write data into uart tx fifo including data and checksum */
hpm_uart_lin_slave_send_data(uart_lin_slave_config_t * config)278 void hpm_uart_lin_slave_send_data(uart_lin_slave_config_t *config)
279 {
280     UART_Type *ptr = config->ptr;
281     uart_lin_data_t data = config->data;
282 
283     assert(data.length > 0);
284 
285     uint8_t checksum = hpm_uart_lin_calculate_checksum(config->pid, data.buff, data.length, data.enhance_checksum);
286 
287     for (uint8_t i = 0; i < data.length; i++) {
288         uart_write_byte(ptr, *(data.buff++));
289     }
290 
291     uart_write_byte(ptr, checksum);
292 }
293 
294 /* read data and checksum */
hpm_uart_lin_slave_receive_data(uart_lin_slave_config_t * config)295 uart_lin_stat_t hpm_uart_lin_slave_receive_data(uart_lin_slave_config_t *config)
296 {
297     UART_Type *ptr = config->ptr;
298     uart_lin_data_t data = config->data;
299 
300     assert(data.length > 0);
301 
302     uint8_t index = 0;
303     uint8_t checksum = 0;
304 
305     /* receive data and checksum */
306     while (uart_check_status(ptr, uart_stat_data_ready)) {
307         if (index == data.length) {
308             checksum = uart_read_byte(ptr);
309             break;
310         }
311         *(data.buff + index++) = uart_read_byte(ptr);
312     }
313 
314     if (index != data.length) {
315         return uart_lin_frame_error;
316     }
317 
318     if (!hpm_uart_lin_check_checksum(config->pid, data.buff, data.length, data.enhance_checksum, checksum)) {
319         return uart_lin_checksum_error;
320     }
321 
322     return uart_lin_success;
323 }