1 // Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <stddef.h>
16 #include <string.h>
17 #include "sdkconfig.h"
18 #include "hal/twai_hal.h"
19 
20 #ifdef CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
21 //Errata condition occurs at 64 messages. Threshold set to 62 to prevent the chance of failing to detect errata condition.
22 #define TWAI_RX_FIFO_CORRUPT_THRESH     62
23 #endif
24 
25 /* ----------------------------- Event Handling ----------------------------- */
26 
27 /**
28  * Helper functions that can decode what events have been triggered based on
29  * the values of the interrupt, status, TEC and REC registers. The HAL context's
30  * state flags are also updated based on the events that have triggered.
31  */
twai_hal_decode_interrupt(twai_hal_context_t * hal_ctx)32 static inline uint32_t twai_hal_decode_interrupt(twai_hal_context_t *hal_ctx)
33 {
34     uint32_t events = 0;
35     uint32_t interrupts = twai_ll_get_and_clear_intrs(hal_ctx->dev);
36     uint32_t status = twai_ll_get_status(hal_ctx->dev);
37     uint32_t tec = twai_ll_get_tec(hal_ctx->dev);
38     uint32_t rec = twai_ll_get_rec(hal_ctx->dev);
39     uint32_t state_flags = hal_ctx->state_flags;
40 
41     //Error Warning Interrupt set whenever Error or Bus Status bit changes
42     if (interrupts & TWAI_LL_INTR_EI) {
43         if (status & TWAI_LL_STATUS_BS) {       //Currently in BUS OFF state
44             if (status & TWAI_LL_STATUS_ES) {    //EWL is exceeded, thus must have entered BUS OFF
45                 TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_OFF);
46                 TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF);
47                 //Any TX would have been halted by entering bus off. Reset its flag
48                 TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RUNNING | TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
49             } else {
50                 //Below EWL. Therefore TEC is counting down in bus recovery
51                 TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_PROGRESS);
52             }
53         } else {    //Not in BUS OFF
54             if (status & TWAI_LL_STATUS_ES) {       //Just Exceeded EWL
55                 TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ABOVE_EWL);
56                 TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN);
57             } else if (hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_RECOVERING) {
58                 //Previously undergoing bus recovery. Thus means bus recovery complete
59                 TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_CPLT);
60                 TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RECOVERING | TWAI_HAL_STATE_FLAG_BUS_OFF);
61             } else {        //Just went below EWL
62                 TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BELOW_EWL);
63                 TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN);
64             }
65         }
66     }
67     //Receive Interrupt set whenever RX FIFO is not empty
68     if (interrupts & TWAI_LL_INTR_RI) {
69         TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_RX_BUFF_FRAME);
70     }
71     //Transmit interrupt set whenever TX buffer becomes free
72 #ifdef CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST
73     if ((interrupts & TWAI_LL_INTR_TI || hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) && status & TWAI_LL_STATUS_TBS) {
74 #else
75     if (interrupts & TWAI_LL_INTR_TI) {
76 #endif
77         TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX_BUFF_FREE);
78         TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
79     }
80     //Error Passive Interrupt on transition from error active to passive or vice versa
81     if (interrupts & TWAI_LL_INTR_EPI) {
82         if (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) {
83             TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_PASSIVE);
84             TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
85         } else {
86             TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_ACTIVE);
87             TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE);
88         }
89     }
90     //Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc)
91     if (interrupts & TWAI_LL_INTR_BEI) {
92         TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_ERR);
93     }
94     //Arbitration Lost Interrupt triggered on losing arbitration
95     if (interrupts & TWAI_LL_INTR_ALI) {
96         TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ARB_LOST);
97     }
98     hal_ctx->state_flags = state_flags;
99     return events;
100 }
101 
102 uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx)
103 {
104     uint32_t events = twai_hal_decode_interrupt(hal_ctx);
105 
106     //Handle low latency events
107     if (events & TWAI_HAL_EVENT_BUS_OFF) {
108         twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY);  //Freeze TEC/REC by entering LOM
109 #ifdef CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC
110         //Errata workaround: Force REC to 0 by re-triggering bus-off (by setting TEC to 0 then 255)
111         twai_ll_set_tec(hal_ctx->dev, 0);
112         twai_ll_set_tec(hal_ctx->dev, 255);
113         (void) twai_ll_get_and_clear_intrs(hal_ctx->dev);    //Clear the re-triggered bus-off interrupt
114 #endif
115     }
116     if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
117         twai_ll_enter_reset_mode(hal_ctx->dev);     //Enter reset mode to stop the controller
118     }
119     if (events & TWAI_HAL_EVENT_BUS_ERR) {
120 #ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID
121         twai_ll_err_type_t type;
122         twai_ll_err_dir_t dir;
123         twai_ll_err_seg_t seg;
124         twai_ll_parse_err_code_cap(hal_ctx->dev, &type, &dir, &seg);    //Decode error interrupt
125         //Check for errata condition (RX message has bus error at particular segments)
126         if (dir == TWAI_LL_ERR_DIR_RX &&
127             ((seg == TWAI_LL_ERR_SEG_DATA || seg == TWAI_LL_ERR_SEG_CRC_SEQ) ||
128              (seg == TWAI_LL_ERR_SEG_ACK_DELIM && type == TWAI_LL_ERR_OTHER))) {
129             TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_NEED_PERIPH_RESET);
130         }
131 #endif
132         twai_ll_clear_err_code_cap(hal_ctx->dev);
133     }
134     if (events & TWAI_HAL_EVENT_ARB_LOST) {
135         twai_ll_clear_arb_lost_cap(hal_ctx->dev);
136     }
137 #ifdef CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
138     //Check for errata condition (rx_msg_count >= corruption_threshold)
139     if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME && twai_ll_get_rx_msg_count(hal_ctx->dev) >= TWAI_RX_FIFO_CORRUPT_THRESH) {
140         TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_NEED_PERIPH_RESET);
141     }
142 #endif
143 #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
144     if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) {
145         //A peripheral reset will invalidate an RX event;
146         TWAI_HAL_CLEAR_BITS(events, (TWAI_HAL_EVENT_RX_BUFF_FRAME));
147     }
148 #endif
149     return events;
150 }
151 
152 #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
153 void twai_hal_prepare_for_reset(twai_hal_context_t *hal_ctx)
154 {
155     uint32_t status = twai_ll_get_status(hal_ctx->dev);
156     if (!(status & TWAI_LL_STATUS_TBS)) {   //Transmit buffer is NOT free, indicating an Ongoing TX will be cancelled by the HW reset
157         TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_NEED_RETRY);
158         //Note: Even if the TX completes right after this, we still consider it will be retried.
159         //Worst case the same message will get sent twice.
160     }
161     //Some register must saved before entering reset mode
162     hal_ctx->rx_msg_cnt_save = (uint8_t) twai_ll_get_rx_msg_count(hal_ctx->dev);
163     twai_ll_enter_reset_mode(hal_ctx->dev);     //Enter reset mode to stop the controller
164     twai_ll_save_reg(hal_ctx->dev, &hal_ctx->reg_save); //Save remaining registers after entering reset mode
165 }
166 
167 void twai_hal_recover_from_reset(twai_hal_context_t *hal_ctx)
168 {
169     twai_ll_enter_reset_mode(hal_ctx->dev);
170     twai_ll_enable_extended_reg_layout(hal_ctx->dev);
171     twai_ll_restore_reg(hal_ctx->dev, &hal_ctx->reg_save);
172     twai_ll_exit_reset_mode(hal_ctx->dev);
173     (void) twai_ll_get_and_clear_intrs(hal_ctx->dev);
174 
175     if (hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_TX_NEED_RETRY) {
176         //HW reset has cancelled a TX. Re-transmit here
177         twai_hal_set_tx_buffer_and_transmit(hal_ctx, &hal_ctx->tx_frame_save);
178         TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_NEED_RETRY);
179     }
180 }
181 #endif  //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
182 
183 void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_frame_t *tx_frame)
184 {
185     //Copy frame into tx buffer
186     twai_ll_set_tx_buffer(hal_ctx->dev, tx_frame);
187     //Hit the send command
188     if (tx_frame->self_reception) {
189         if (tx_frame->single_shot) {
190             twai_ll_set_cmd_self_rx_single_shot(hal_ctx->dev);
191         } else {
192             twai_ll_set_cmd_self_rx_request(hal_ctx->dev);
193         }
194     } else if (tx_frame->single_shot){
195         twai_ll_set_cmd_tx_single_shot(hal_ctx->dev);
196     } else {
197         twai_ll_set_cmd_tx(hal_ctx->dev);
198     }
199     TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
200 #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
201     //Save transmitted frame in case we need to retry
202     memcpy(&hal_ctx->tx_frame_save, tx_frame, sizeof(twai_hal_frame_t));
203 #endif  //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
204 }
205