1 | /***************************************************************** |
---|
2 | * File: wlan_axi_ethernet_intr.c |
---|
3 | * Copyright (c) 2019 Mango Communications, all rights reserved |
---|
4 | *****************************************************************/ |
---|
5 | |
---|
6 | #include "wlan_axi_ethernet_intr.h" |
---|
7 | #include "wlan_mac_common.h" |
---|
8 | #include "wlan_mac_dl_list.h" |
---|
9 | #include "wlan_mac_eth_util.h" |
---|
10 | #include "wlan_mac_queue.h" |
---|
11 | #include "wlan_mac_high.h" |
---|
12 | #include "wlan_platform_high.h" |
---|
13 | #include "wlan_platform_intc.h" |
---|
14 | #include "wlan_common_types.h" |
---|
15 | |
---|
16 | #include "xaxiethernet.h" |
---|
17 | #include "xaxidma.h" |
---|
18 | #include "xaxidma_hw.h" |
---|
19 | |
---|
20 | // Local function declarations |
---|
21 | static int _init_rx_bd(XAxiDma_Bd* bd_ptr, dl_entry* qe_ptr, u32 max_transfer_len, u32 LengthMask); |
---|
22 | static u32 _wlan_axi_eth_intr_calc_num_bd(u32 mem_size); |
---|
23 | static void _eth_rx_interrupt_handler(void *callback_arg); |
---|
24 | static void _eth_tx_interrupt_handler(void *callback_arg); |
---|
25 | inline void _wlan_axi_eth_intr_free_tx_bd(peripheral_args_t* peripheral_args); |
---|
26 | |
---|
27 | /** |
---|
28 | * @brief Initialize AXI ETHERNET INTR with Interrupts |
---|
29 | * |
---|
30 | * This function initializes the AXI ETHERNET INTR peripheral. |
---|
31 | * |
---|
32 | * @param peripheral_args_t* peripheral_args |
---|
33 | * - necessary arguments to distinguish between multiple instances of this peripheral |
---|
34 | * u32 eth_device_id |
---|
35 | * - The Device ID of the AXI ETHERNET known to platform code (typically from xparameters.h) |
---|
36 | * u32 wlan_eth_link_speed |
---|
37 | * - Link speed (Mbps) |
---|
38 | * u32 wlan_eth_mdio_phyaddr |
---|
39 | * - Offset of MDIO_PHYADDR register |
---|
40 | * u32 eth_bd_mem_base |
---|
41 | * - The base address where this peripheral can store Ethernet buffer descriptors |
---|
42 | * u32 eth_bd_mem_size |
---|
43 | * - The total space (in bytes) available at the provided base address |
---|
44 | * u32 num_tx_bd |
---|
45 | * - Number of Tx BDs that will be used. The remaining portion of eth_bd_mem_size |
---|
46 | * will be used for Rx BDs |
---|
47 | * u32 tx_coalescing_count |
---|
48 | * - Number of Tx events that will be coalesced into a single interrupt |
---|
49 | * u32 tx_coalescing_timer |
---|
50 | * - If tx_coalescing_count is not achieved but there are complete Tx events, an interrupt |
---|
51 | * will occur after this amount of time after the last reception. The units of this |
---|
52 | * parameter are platform specific. Namely, the resolution is C_DLYTMR_RESOLUTION cycles |
---|
53 | * of the m_axi_sg_aclk clock |
---|
54 | * @return intr_conn_params_t |
---|
55 | * - A struct containing a pointer to a ISR handler and an ISR argument that platform code |
---|
56 | * should use when connecting the peripheral to the interrupt controller |
---|
57 | */ |
---|
58 | intr_conn_params_t wlan_axi_eth_intr_init( peripheral_args_t* peripheral_args, |
---|
59 | u32 eth_device_id, |
---|
60 | u32 wlan_eth_link_speed, |
---|
61 | u32 wlan_eth_mdio_phyaddr, |
---|
62 | u32 eth_bd_mem_base, |
---|
63 | u32 eth_bd_mem_size, |
---|
64 | u32 num_tx_bd, |
---|
65 | u32 tx_coalescing_count, |
---|
66 | u32 tx_coalescing_timer ){ |
---|
67 | intr_conn_params_t intr_conn_params; |
---|
68 | XAxiDma* eth_dma_instance_ptr; |
---|
69 | XAxiEthernet_Config* eth_cfg_ptr; |
---|
70 | XAxiEthernet eth_instance; |
---|
71 | XAxiDma_Config* eth_dma_cfg_ptr; |
---|
72 | XAxiDma_Bd eth_dma_bd_template; |
---|
73 | XAxiDma_BdRing* eth_tx_ring_ptr; |
---|
74 | XAxiDma_BdRing* eth_rx_ring_ptr; |
---|
75 | XAxiDma_Bd* bd_ptr; |
---|
76 | XAxiDma_Bd* first_bd_ptr; |
---|
77 | dl_list rx_queue_list; |
---|
78 | dl_entry* curr_queue_element; |
---|
79 | dl_entry* next_queue_element; |
---|
80 | u32 bd_count; |
---|
81 | u32 i; |
---|
82 | u32 eth_tx_bd_mem_base; |
---|
83 | u32 eth_tx_bd_mem_size; |
---|
84 | u32 eth_tx_bd_mem_high; |
---|
85 | u32 eth_rx_bd_mem_base; |
---|
86 | u32 eth_rx_bd_mem_size; |
---|
87 | u32 num_rx_bd; |
---|
88 | int status; |
---|
89 | |
---|
90 | intr_conn_params.intr_handler0 = NULL; |
---|
91 | intr_conn_params.intr_handler_arg0 = NULL; |
---|
92 | intr_conn_params.intr_handler1 = NULL; |
---|
93 | intr_conn_params.intr_handler_arg1 = NULL; |
---|
94 | |
---|
95 | eth_dma_instance_ptr = (XAxiDma*)peripheral_args->driver_instance; |
---|
96 | |
---|
97 | // Split up the memory set aside for us in eth_bd_mem_base |
---|
98 | eth_tx_bd_mem_base = eth_bd_mem_base; |
---|
99 | eth_tx_bd_mem_size = num_tx_bd*XAXIDMA_BD_MINIMUM_ALIGNMENT; |
---|
100 | eth_tx_bd_mem_high = CALC_MEM_HIGH_ADDR(eth_tx_bd_mem_base, eth_tx_bd_mem_size); |
---|
101 | eth_rx_bd_mem_base = eth_tx_bd_mem_high+1; |
---|
102 | eth_rx_bd_mem_size = eth_bd_mem_size - eth_tx_bd_mem_size; |
---|
103 | |
---|
104 | if(num_tx_bd == 0){ |
---|
105 | xil_printf("WLAN_AXI_ETHERNET_INTR: Tx BD allocation failed, insufficient memory\n"); |
---|
106 | wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CODE_INSUFFICIENT_BD_SIZE); |
---|
107 | return intr_conn_params; |
---|
108 | } |
---|
109 | |
---|
110 | num_rx_bd = _wlan_axi_eth_intr_calc_num_bd(eth_rx_bd_mem_size); |
---|
111 | if(num_rx_bd == 0){ |
---|
112 | xil_printf("WLAN_AXI_ETHERNET_INTR: Rx BD allocation failed, insufficient memory\n"); |
---|
113 | wlan_platform_high_userio_disp_status(USERIO_DISP_STATUS_CPU_ERROR, WLAN_ERROR_CODE_INSUFFICIENT_BD_SIZE); |
---|
114 | return intr_conn_params; |
---|
115 | } |
---|
116 | |
---|
117 | // Initialize buffer descriptor counts |
---|
118 | //xil_printf("WLAN_AXI_ETHERNET_INTR: %3d Eth Tx BDs placed in 0x%08x: using %d B\n", num_tx_bd, eth_tx_bd_mem_base, num_tx_bd*XAXIDMA_BD_MINIMUM_ALIGNMENT); |
---|
119 | //xil_printf("WLAN_AXI_ETHERNET_INTR: %3d Eth Rx BDs placed in 0x%08x: using %d kB\n", num_rx_bd, eth_rx_bd_mem_base, num_rx_bd*XAXIDMA_BD_MINIMUM_ALIGNMENT/1024); |
---|
120 | |
---|
121 | // Initialize Ethernet instance |
---|
122 | eth_cfg_ptr = XAxiEthernet_LookupConfig(eth_device_id); |
---|
123 | status = XAxiEthernet_CfgInitialize(ð_instance, eth_cfg_ptr, eth_cfg_ptr->BaseAddress); |
---|
124 | if (status != XST_SUCCESS) { |
---|
125 | xil_printf("Error in XAxiEthernet_CfgInitialize! Err = %d\n", status); |
---|
126 | return intr_conn_params; |
---|
127 | }; |
---|
128 | |
---|
129 | // Setup the Ethernet options |
---|
130 | // NOTE: This Ethernet driver does support jumbo Ethernet frames. Only 2KB is allocated for each Rx buffer |
---|
131 | status = XAxiEthernet_ClearOptions(ð_instance, XAE_LENTYPE_ERR_OPTION | XAE_FLOW_CONTROL_OPTION ); |
---|
132 | status |= XAxiEthernet_SetOptions(ð_instance, XAE_JUMBO_OPTION | XAE_FCS_STRIP_OPTION | XAE_PROMISC_OPTION | XAE_MULTICAST_OPTION | XAE_BROADCAST_OPTION | XAE_FCS_INSERT_OPTION); |
---|
133 | status |= XAxiEthernet_SetOptions(ð_instance, XAE_RECEIVER_ENABLE_OPTION | XAE_TRANSMITTER_ENABLE_OPTION); |
---|
134 | |
---|
135 | if (status != XST_SUCCESS){ |
---|
136 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiEthernet_Set/ClearOptions! Err = %d\n", status); |
---|
137 | return intr_conn_params; |
---|
138 | }; |
---|
139 | |
---|
140 | // Set the operating speed |
---|
141 | XAxiEthernet_SetOperatingSpeed(ð_instance, wlan_eth_link_speed); |
---|
142 | |
---|
143 | // If the link speed is 1 Gbps, then only advertise and link to 1 Gbps connection |
---|
144 | // See Ethernet PHY specification for documentation on the values used |
---|
145 | if (wlan_eth_link_speed == 1000) { |
---|
146 | XAxiEthernet_PhyWrite(ð_instance, wlan_eth_mdio_phyaddr, 0, 0x0140); |
---|
147 | XAxiEthernet_PhyWrite(ð_instance, wlan_eth_mdio_phyaddr, 0, 0x8140); |
---|
148 | } |
---|
149 | |
---|
150 | // Initialize the DMA |
---|
151 | |
---|
152 | // Initialize the DMA peripheral structures |
---|
153 | eth_dma_cfg_ptr = XAxiDma_LookupConfig(eth_device_id); |
---|
154 | status = XAxiDma_CfgInitialize(eth_dma_instance_ptr, eth_dma_cfg_ptr); |
---|
155 | if (status != XST_SUCCESS) { |
---|
156 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_CfgInitialize! Err = %d\n", status); |
---|
157 | return intr_conn_params; |
---|
158 | } |
---|
159 | |
---|
160 | // Zero-out the template buffer descriptor |
---|
161 | XAxiDma_BdClear(ð_dma_bd_template); |
---|
162 | |
---|
163 | // Fetch handles to the Tx and Rx BD rings |
---|
164 | eth_tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr); |
---|
165 | eth_rx_ring_ptr = XAxiDma_GetRxRing(eth_dma_instance_ptr); |
---|
166 | |
---|
167 | // Disable all Tx/Rx DMA interrupts |
---|
168 | XAxiDma_BdRingIntDisable(eth_tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK); |
---|
169 | XAxiDma_BdRingIntDisable(eth_rx_ring_ptr, XAXIDMA_IRQ_ALL_MASK); |
---|
170 | |
---|
171 | // Disable Rx delay and coalescing |
---|
172 | // - We have tried having these on but have not observed any appreciable |
---|
173 | // performance improvement |
---|
174 | // - Intuitively, both sinks for Ethernet traffic (802.11 portal and wlan_exp |
---|
175 | // processing are slower than the Ethernet line rate so there isn't much to |
---|
176 | // gain |
---|
177 | XAxiDma_BdRingSetCoalesce(eth_rx_ring_ptr, 1, 0); |
---|
178 | |
---|
179 | // Enable Tx delay and coalescing. |
---|
180 | XAxiDma_BdRingSetCoalesce(eth_tx_ring_ptr, tx_coalescing_count, tx_coalescing_timer); |
---|
181 | |
---|
182 | // Setup Tx/Rx buffer descriptor rings in memory |
---|
183 | status = XAxiDma_BdRingCreate(eth_tx_ring_ptr, eth_tx_bd_mem_base, eth_tx_bd_mem_base, XAXIDMA_BD_MINIMUM_ALIGNMENT, num_tx_bd); |
---|
184 | status |= XAxiDma_BdRingCreate(eth_rx_ring_ptr, eth_rx_bd_mem_base, eth_rx_bd_mem_base, XAXIDMA_BD_MINIMUM_ALIGNMENT, num_rx_bd); |
---|
185 | |
---|
186 | if (status != XST_SUCCESS) { |
---|
187 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error creating DMA BD Rings! Err = %d\n", status); |
---|
188 | return intr_conn_params; |
---|
189 | } |
---|
190 | |
---|
191 | // Populate each ring with empty buffer descriptors |
---|
192 | status = XAxiDma_BdRingClone(eth_tx_ring_ptr, ð_dma_bd_template); |
---|
193 | status |= XAxiDma_BdRingClone(eth_rx_ring_ptr, ð_dma_bd_template); |
---|
194 | if (status != XST_SUCCESS) { |
---|
195 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingClone()! Err = %d\n", status); |
---|
196 | return intr_conn_params; |
---|
197 | } |
---|
198 | |
---|
199 | // Initialize the Rx buffer descriptors |
---|
200 | bd_count = XAxiDma_BdRingGetFreeCnt(eth_rx_ring_ptr); |
---|
201 | if (bd_count != num_rx_bd) { |
---|
202 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in Eth Rx DMA init - not all Rx BDs were free at boot\n"); |
---|
203 | } |
---|
204 | |
---|
205 | status = XAxiDma_BdRingAlloc(eth_rx_ring_ptr, bd_count, &bd_ptr); |
---|
206 | if (status != XST_SUCCESS) { |
---|
207 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingAlloc()! Err = %d\n", status); |
---|
208 | return intr_conn_params; |
---|
209 | } |
---|
210 | |
---|
211 | first_bd_ptr = bd_ptr; // We need to hold on to the first one when submitting the ring |
---|
212 | // to the hardware. |
---|
213 | |
---|
214 | // Checkout bd_count of free queue entries to attach to these buffer descriptors |
---|
215 | dl_list_init(&rx_queue_list); |
---|
216 | queue_checkout_list(&rx_queue_list, num_rx_bd); |
---|
217 | |
---|
218 | if(rx_queue_list.length != num_rx_bd){ |
---|
219 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error checking out queue entries during Eth peripheral init\n"); |
---|
220 | queue_checkin_list(&rx_queue_list); |
---|
221 | return intr_conn_params; |
---|
222 | } |
---|
223 | |
---|
224 | |
---|
225 | next_queue_element = rx_queue_list.first; |
---|
226 | |
---|
227 | // Loop bd_count buffer descriptors and attach them to queue entries |
---|
228 | // provided in rx_queue_list |
---|
229 | for (i = 0; i < num_rx_bd; i++) { |
---|
230 | // Take a queue element from the list provided as an argument |
---|
231 | curr_queue_element = next_queue_element; |
---|
232 | next_queue_element = dl_entry_next(next_queue_element); |
---|
233 | |
---|
234 | if (curr_queue_element == NULL) { |
---|
235 | xil_printf("WLAN_AXI_ETHERNET_INTR: Insufficient elements in rx_queue_list\n"); |
---|
236 | queue_checkin_list(&rx_queue_list); |
---|
237 | return intr_conn_params; |
---|
238 | } |
---|
239 | |
---|
240 | // Remove the queue element from the list. Rx handling will be responsible for returning |
---|
241 | // it to a list at some point |
---|
242 | dl_entry_remove(&rx_queue_list, curr_queue_element); |
---|
243 | |
---|
244 | |
---|
245 | // Initialize the Rx buffer descriptor |
---|
246 | status = _init_rx_bd(bd_ptr, curr_queue_element, WLAN_AXI_ETH_RX_INTR_BUF_SIZE, eth_rx_ring_ptr->MaxTransferLen); |
---|
247 | |
---|
248 | if (status != XST_SUCCESS) { |
---|
249 | xil_printf("Error initializing Rx BD %d\n", i); |
---|
250 | queue_checkin_list(&rx_queue_list); |
---|
251 | return intr_conn_params; |
---|
252 | } |
---|
253 | |
---|
254 | // Update bd_ptr to the next BD in the chain for the next iteration |
---|
255 | bd_ptr = XAxiDma_BdRingNext(eth_rx_ring_ptr, bd_ptr); |
---|
256 | } |
---|
257 | |
---|
258 | // Push the Rx BD ring to hardware and start receiving |
---|
259 | status = XAxiDma_BdRingToHw(eth_rx_ring_ptr, bd_count, first_bd_ptr); |
---|
260 | |
---|
261 | if (status != XST_SUCCESS) { |
---|
262 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error submitting Rx BD ring to HW\n"); |
---|
263 | queue_checkin_list(&rx_queue_list); |
---|
264 | return intr_conn_params; |
---|
265 | } |
---|
266 | |
---|
267 | // Enable Interrupts |
---|
268 | XAxiDma_BdRingIntEnable(eth_rx_ring_ptr, XAXIDMA_IRQ_ALL_MASK); |
---|
269 | XAxiDma_BdRingIntEnable(eth_tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK); |
---|
270 | |
---|
271 | status |= XAxiDma_BdRingStart(eth_rx_ring_ptr); |
---|
272 | if (status != XST_SUCCESS){ |
---|
273 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma BdRingToHw/BdRingStart! Err = %d\n", status); |
---|
274 | return intr_conn_params; |
---|
275 | } |
---|
276 | |
---|
277 | // Start the DMA Tx channel |
---|
278 | // NOTE: No Eth packets are transmitted until actual Tx BD's are pushed to the DMA hardware |
---|
279 | status = XAxiDma_BdRingStart(eth_tx_ring_ptr); |
---|
280 | |
---|
281 | |
---|
282 | // Start the Ethernet controller |
---|
283 | XAxiEthernet_Start(ð_instance); |
---|
284 | |
---|
285 | // Fill in return values |
---|
286 | intr_conn_params.intr_handler0 = (function_ptr_t)_eth_rx_interrupt_handler; |
---|
287 | intr_conn_params.intr_handler_arg0 = (void*)peripheral_args; |
---|
288 | intr_conn_params.intr_handler1 = (function_ptr_t)_eth_tx_interrupt_handler; |
---|
289 | intr_conn_params.intr_handler_arg1 = (void*)peripheral_args; |
---|
290 | |
---|
291 | return intr_conn_params; |
---|
292 | } |
---|
293 | |
---|
294 | /** |
---|
295 | * @brief Send Ethernet Frame |
---|
296 | * |
---|
297 | * This function sends an Ethernet frame. It does not perform error checking on the validity |
---|
298 | * of the provided address. It is the responsibility of platform code to error check the arguments |
---|
299 | * provided to this function. |
---|
300 | * |
---|
301 | * @param peripheral_args_t* peripheral_args |
---|
302 | * - necessary arguments to distinguish between multiple instances of this peripheral |
---|
303 | * @param eth_tx_queue_buffer_t* eth_tx_queue_buffer |
---|
304 | * - Ethernet packet to send |
---|
305 | * @return u32 status |
---|
306 | * - see header file |
---|
307 | */ |
---|
308 | inline u32 wlan_axi_eth_intr_send(peripheral_args_t* peripheral_args, eth_tx_queue_buffer_t* eth_tx_queue_buffer) { |
---|
309 | |
---|
310 | int status; |
---|
311 | XAxiDma_BdRing* tx_ring_ptr; |
---|
312 | XAxiDma_Bd* cur_bd_ptr; |
---|
313 | XAxiDma_Bd* first_bd_ptr; |
---|
314 | u8* bd_addr[2]; |
---|
315 | u16 bd_len[2]; |
---|
316 | u8 num_required_bd, idx_bd; |
---|
317 | u32 length; |
---|
318 | u32 ctrl; |
---|
319 | XAxiDma* eth_dma_instance_ptr; |
---|
320 | |
---|
321 | interrupt_state_t prev_interrupt_state; |
---|
322 | |
---|
323 | bd_addr[0] = NULL; |
---|
324 | bd_addr[1] = NULL; |
---|
325 | bd_len[0] = 0; |
---|
326 | bd_len[1] = 0; |
---|
327 | num_required_bd = 0; |
---|
328 | ctrl = 0; |
---|
329 | |
---|
330 | eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance); |
---|
331 | |
---|
332 | length = eth_tx_queue_buffer->seg0_len + eth_tx_queue_buffer->seg1_len; |
---|
333 | |
---|
334 | if( (length < 42) || (length > 9014) ){ |
---|
335 | xil_printf("ERROR (wlan_axi_eth_intr_send): Length %d out of range\n", length); |
---|
336 | return WLAN_AXI_ETH_INTR_SEND_ERR_INVALID_ARGS; |
---|
337 | } |
---|
338 | |
---|
339 | |
---|
340 | prev_interrupt_state = wlan_platform_intc_stop(); |
---|
341 | |
---|
342 | // Get pointer to the axi_dma Tx buffer descriptor ring |
---|
343 | tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr); |
---|
344 | |
---|
345 | if(eth_tx_queue_buffer->seg0_len > 0){ |
---|
346 | bd_addr[num_required_bd] = eth_tx_queue_buffer->seg0; |
---|
347 | bd_len[num_required_bd] = eth_tx_queue_buffer->seg0_len; |
---|
348 | num_required_bd++; |
---|
349 | } |
---|
350 | if(eth_tx_queue_buffer->seg1_len > 0){ |
---|
351 | bd_addr[num_required_bd] = eth_tx_queue_buffer->seg1_addr; |
---|
352 | bd_len[num_required_bd] = eth_tx_queue_buffer->seg1_len; |
---|
353 | num_required_bd++; |
---|
354 | } |
---|
355 | |
---|
356 | // Allocate and setup one Tx BD |
---|
357 | status = XAxiDma_BdRingAlloc(tx_ring_ptr, num_required_bd, &cur_bd_ptr); |
---|
358 | |
---|
359 | if(status != XST_SUCCESS) { |
---|
360 | // There are no free BDs. Rather than block here, we'll return and allow |
---|
361 | // the calling context to try transmitting later |
---|
362 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
363 | return WLAN_AXI_ETH_INTR_SEND_ERR_NO_FREE_BD; |
---|
364 | } |
---|
365 | |
---|
366 | first_bd_ptr = cur_bd_ptr; |
---|
367 | |
---|
368 | for(idx_bd = 0; idx_bd < num_required_bd; idx_bd++){ |
---|
369 | // If this is a single BD transmission, both flags will be raised. |
---|
370 | ctrl = 0; |
---|
371 | if(idx_bd == 0) ctrl |= XAXIDMA_BD_CTRL_TXSOF_MASK; |
---|
372 | if(idx_bd == (num_required_bd-1)) ctrl |= XAXIDMA_BD_CTRL_TXEOF_MASK; |
---|
373 | |
---|
374 | status = XAxiDma_BdSetBufAddr(cur_bd_ptr, (u32)bd_addr[idx_bd]); |
---|
375 | status |= XAxiDma_BdSetLength(cur_bd_ptr, bd_len[idx_bd], tx_ring_ptr->MaxTransferLen); |
---|
376 | |
---|
377 | // TODO: this is a cache-sensitive context. In architectures with the cache enabled, |
---|
378 | // we should flush the cashe for the range covered in this BD. |
---|
379 | //XCACHE_FLUSH_DCACHE_RANGE(bd_addr[idx_bd], bd_len[idx_bd]); |
---|
380 | |
---|
381 | if(status != XST_SUCCESS) { |
---|
382 | xil_printf("WLAN_AXI_ETHERNET_INTR: ERROR setting ETH TX BD! Err = %d\n", status); |
---|
383 | status = XAxiDma_BdRingFree(tx_ring_ptr, num_required_bd, first_bd_ptr); |
---|
384 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
385 | return WLAN_AXI_ETH_INTR_SEND_ERR_OTHER; |
---|
386 | } |
---|
387 | |
---|
388 | XAxiDma_BdSetCtrl(cur_bd_ptr, ctrl); |
---|
389 | cur_bd_ptr = (XAxiDma_Bd*)XAxiDma_BdRingNext(tx_ring_ptr, cur_bd_ptr); |
---|
390 | } |
---|
391 | |
---|
392 | // Push the BD ring to hardware; this initiates the actual DMA transfer and Ethernet Tx |
---|
393 | status = XAxiDma_BdRingToHw(tx_ring_ptr, num_required_bd, first_bd_ptr); |
---|
394 | if(status != XST_SUCCESS){ |
---|
395 | xil_printf("WLAN_AXI_ETHERNET_INTR: ERROR: TX XAxiDma_BdRingToHw! Err = %d\n", status); |
---|
396 | status = XAxiDma_BdRingFree(tx_ring_ptr, num_required_bd, first_bd_ptr); |
---|
397 | return WLAN_AXI_ETH_INTR_SEND_ERR_OTHER; |
---|
398 | } |
---|
399 | |
---|
400 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
401 | return WLAN_SUCCESS; |
---|
402 | } |
---|
403 | |
---|
404 | |
---|
405 | /** |
---|
406 | * @brief Attach any free buffer descriptors |
---|
407 | * |
---|
408 | * This function may be called at any time to attach any free buffer descriptors to free queue entries |
---|
409 | * provided by the High MAC Framework. If either no buffer descriptors are free or there are no free |
---|
410 | * queue entries, this function will successfully do nothing. |
---|
411 | * |
---|
412 | * @param peripheral_args_t* peripheral_args |
---|
413 | * - necessary arguments to distinguish between multiple instances of this peripheral |
---|
414 | * @return none |
---|
415 | */ |
---|
416 | void wlan_axi_eth_intr_attach_free_rx_bd(peripheral_args_t* peripheral_args) { |
---|
417 | int status; |
---|
418 | int iter; |
---|
419 | u32 bd_count; |
---|
420 | u32 bd_queue_pairs_to_process; |
---|
421 | u32 bd_queue_pairs_processed; |
---|
422 | |
---|
423 | XAxiDma* eth_dma_instance_ptr; |
---|
424 | XAxiDma_BdRing* rx_ring_ptr; |
---|
425 | XAxiDma_Bd* first_bd_ptr; |
---|
426 | XAxiDma_Bd* cur_bd_ptr; |
---|
427 | |
---|
428 | dl_list checkout; |
---|
429 | dl_entry* queue_entry; |
---|
430 | |
---|
431 | interrupt_state_t prev_interrupt_state = wlan_platform_intc_stop(); |
---|
432 | |
---|
433 | eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance); |
---|
434 | |
---|
435 | // Get the Rx ring pointer and the number of free Rx buffer descriptors |
---|
436 | rx_ring_ptr = XAxiDma_GetRxRing(eth_dma_instance_ptr); |
---|
437 | bd_count = XAxiDma_BdRingGetFreeCnt(rx_ring_ptr); |
---|
438 | |
---|
439 | // If there are no BDs, then we are done |
---|
440 | if (bd_count == 0) { |
---|
441 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
442 | return; |
---|
443 | } |
---|
444 | |
---|
445 | // Initialize the list to checkout Tx queue entries |
---|
446 | dl_list_init(&checkout); |
---|
447 | |
---|
448 | // Attempt to checkout Tx queue entries for all free buffer descriptors |
---|
449 | queue_checkout_list(&checkout, bd_count); |
---|
450 | |
---|
451 | // If there were not enough Tx queue entries available, the length of the |
---|
452 | // checkout list will be the number of free Tx queue entries that were |
---|
453 | // found. Only process buffer descriptors that have a corresponding Tx |
---|
454 | // queue entry. |
---|
455 | bd_queue_pairs_to_process = WLAN_MIN(bd_count, checkout.length); |
---|
456 | |
---|
457 | if (bd_queue_pairs_to_process > 0) { |
---|
458 | |
---|
459 | // Initialize the number of buffer descriptors processed |
---|
460 | bd_queue_pairs_processed = 0; |
---|
461 | |
---|
462 | // Allocate the correct number of Rx buffer descriptors that have |
---|
463 | // been freed and have Tx queue entries available. |
---|
464 | status = XAxiDma_BdRingAlloc(rx_ring_ptr, bd_queue_pairs_to_process, &first_bd_ptr); |
---|
465 | if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingAlloc()! Err = %d\n", status); |
---|
466 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
467 | return; |
---|
468 | } |
---|
469 | |
---|
470 | // Initialize loop variables |
---|
471 | iter = checkout.length; |
---|
472 | queue_entry = checkout.first; |
---|
473 | cur_bd_ptr = first_bd_ptr; |
---|
474 | |
---|
475 | while ((queue_entry != NULL) && (iter-- > 0)) { |
---|
476 | |
---|
477 | // Initialize the Rx buffer descriptor |
---|
478 | status = _init_rx_bd(cur_bd_ptr, queue_entry, WLAN_AXI_ETH_RX_INTR_BUF_SIZE, rx_ring_ptr->MaxTransferLen); |
---|
479 | |
---|
480 | if (status != XST_SUCCESS) { |
---|
481 | // Clean up buffer descriptors and Tx queues |
---|
482 | // NOTE: Regardless of where we are in the update, we will check |
---|
483 | // back in everything so the function can start fresh next invocation |
---|
484 | status = XAxiDma_BdRingUnAlloc(rx_ring_ptr, bd_queue_pairs_to_process, first_bd_ptr); |
---|
485 | if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingUnAlloc()! Err = %d\n", status);} |
---|
486 | |
---|
487 | queue_checkin_list(&checkout); |
---|
488 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
489 | return; |
---|
490 | } |
---|
491 | |
---|
492 | // Update the BD and queue entry pointers to the next list elements |
---|
493 | // NOTE: This loop traverses both lists simultaneously |
---|
494 | cur_bd_ptr = XAxiDma_BdRingNext(rx_ring_ptr, cur_bd_ptr); |
---|
495 | queue_entry = dl_entry_next(queue_entry); |
---|
496 | bd_queue_pairs_processed++; |
---|
497 | |
---|
498 | } |
---|
499 | |
---|
500 | if (bd_queue_pairs_processed == bd_queue_pairs_to_process) { |
---|
501 | // Push the Rx BD ring to hardware and start receiving |
---|
502 | status = XAxiDma_BdRingToHw(rx_ring_ptr, bd_queue_pairs_to_process, first_bd_ptr); |
---|
503 | if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: XAxiDma_BdRingToHw failed! Err = %d\n", status);} |
---|
504 | } else { |
---|
505 | // Clean up buffer descriptors and Tx queues |
---|
506 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error processing BD-queue pairs\n"); |
---|
507 | |
---|
508 | status = XAxiDma_BdRingUnAlloc(rx_ring_ptr, bd_queue_pairs_to_process, first_bd_ptr); |
---|
509 | if(status != XST_SUCCESS) {xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingUnAlloc()! Err = %d\n", status);} |
---|
510 | |
---|
511 | queue_checkin_list(&checkout); |
---|
512 | } |
---|
513 | } |
---|
514 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
515 | } |
---|
516 | |
---|
517 | int wlan_axi_eth_intr_get_mtu(){ |
---|
518 | return 9000; |
---|
519 | } |
---|
520 | |
---|
521 | // Local Helper Functions |
---|
522 | |
---|
523 | /*****************************************************************************/ |
---|
524 | /** |
---|
525 | * @brief Initializes an Rx buffer descriptor to use the given Tx queue entry |
---|
526 | * |
---|
527 | * @param XAxiDma_Bd * bd_ptr |
---|
528 | * - Pointer to buffer descriptor to be initialized |
---|
529 | * @param dl_entry * qe_ptr |
---|
530 | * - Pointer to Tx queue element |
---|
531 | * @param u32 max_transfer_len |
---|
532 | * - Max transfer length for Rx BD |
---|
533 | * @param u32 LengthMask |
---|
534 | * - Mask for length in Rx BD |
---|
535 | * |
---|
536 | * @return WLAN_SUCCESS or WLAN_FAILURE |
---|
537 | */ |
---|
538 | static int _init_rx_bd(XAxiDma_Bd* bd_ptr, dl_entry* qe_ptr, u32 max_transfer_len, u32 LengthMask) { |
---|
539 | int status; |
---|
540 | u32 buf_addr; |
---|
541 | |
---|
542 | if ((bd_ptr == NULL) || (qe_ptr == NULL)) { return WLAN_FAILURE; } |
---|
543 | |
---|
544 | // Set the memory address for this BD's buffer to the corresponding Tx queue entry buffer |
---|
545 | // NOTE: This pointer is offset by the size of a MAC header and LLC header, which results |
---|
546 | // in the Ethernet payload being copied to its post-encapsulated location. This |
---|
547 | // speeds up the encapsulation process by skipping any re-copying of Ethernet payloads |
---|
548 | buf_addr = (u32)((void*)((eth_rx_queue_buffer_t*)(qe_ptr->data))->pkt); |
---|
549 | |
---|
550 | status = XAxiDma_BdSetBufAddr(bd_ptr, buf_addr); |
---|
551 | if (status != XST_SUCCESS) { xil_printf("WLAN_AXI_ETHERNET_INTR: XAxiDma_BdSetBufAddr failed (addr 0x08x)! Err = %d\n", buf_addr, status); return WLAN_FAILURE; } |
---|
552 | |
---|
553 | // Set every Rx BD to max length (this assures 1 BD per Rx pkt) |
---|
554 | // NOTE: Jumbo Ethernet frames are not supported by the Ethernet device (ie the XAE_JUMBO_OPTION is cleared), |
---|
555 | // so the WLAN_ETH_PKT_BUF_SIZE must be at least large enough to support standard MTUs (ie greater than |
---|
556 | // 1522 bytes) so the assumption of 1 BD = 1 Rx pkt is met. |
---|
557 | status = XAxiDma_BdSetLength(bd_ptr, max_transfer_len, LengthMask); |
---|
558 | if (status != XST_SUCCESS) { xil_printf("WLAN_AXI_ETHERNET_INTR: XAxiDma_BdSetLength failed (addr 0x08x)! Err = %d\n", buf_addr, status); return WLAN_FAILURE; } |
---|
559 | |
---|
560 | // Rx BD's don't need control flags before use; DMA populates these post-Rx |
---|
561 | XAxiDma_BdSetCtrl(bd_ptr, 0); |
---|
562 | |
---|
563 | return WLAN_SUCCESS; |
---|
564 | } |
---|
565 | |
---|
566 | static u32 _wlan_axi_eth_intr_calc_num_bd(u32 mem_size){ |
---|
567 | return (mem_size/XAXIDMA_BD_MINIMUM_ALIGNMENT); |
---|
568 | } |
---|
569 | |
---|
570 | static void _eth_tx_interrupt_handler(void *callback_arg){ |
---|
571 | XAxiDma_BdRing* tx_ring_ptr; |
---|
572 | u32 irq_status; |
---|
573 | XAxiDma* eth_dma_instance_ptr; |
---|
574 | |
---|
575 | peripheral_args_t* peripheral_args = (peripheral_args_t*)callback_arg; |
---|
576 | eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance); |
---|
577 | |
---|
578 | tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr); |
---|
579 | |
---|
580 | XAxiDma_BdRingIntDisable(tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK); |
---|
581 | irq_status = XAxiDma_BdRingGetIrq(tx_ring_ptr); |
---|
582 | |
---|
583 | XAxiDma_BdRingAckIrq(tx_ring_ptr, irq_status); |
---|
584 | |
---|
585 | if (irq_status & XAXIDMA_IRQ_ERROR_MASK) { |
---|
586 | xil_printf("WLAN_AXI_ETHERNET_INTR: %s: error interrupt asserted\n", __FUNCTION__); |
---|
587 | XAxiDma_Reset(eth_dma_instance_ptr); |
---|
588 | |
---|
589 | return; |
---|
590 | } |
---|
591 | |
---|
592 | if (irq_status & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK)) { |
---|
593 | _wlan_axi_eth_intr_free_tx_bd(peripheral_args); |
---|
594 | } |
---|
595 | |
---|
596 | XAxiDma_BdRingIntEnable(tx_ring_ptr, XAXIDMA_IRQ_ALL_MASK); |
---|
597 | |
---|
598 | |
---|
599 | } |
---|
600 | |
---|
601 | static void _eth_rx_interrupt_handler(void *callback_arg){ |
---|
602 | XAxiDma_BdRing* rx_ring_ptr; |
---|
603 | u32 irq_status; |
---|
604 | XAxiDma_Bd* bd_set_to_process_ptr; |
---|
605 | u32 eth_rx_len; |
---|
606 | u32 eth_rx_buf; |
---|
607 | u32 bd_set_count; |
---|
608 | u32 status; |
---|
609 | eth_rx_queue_buffer_t* eth_rx_queue_buffer; |
---|
610 | XAxiDma* eth_dma_instance_ptr; |
---|
611 | |
---|
612 | |
---|
613 | // FIXME: In the case of >WLAN_AXI_ETH_RX_INTR_BUF_SIZE receptions, |
---|
614 | // packet won't be dropped but rather split across multiple BDs. We |
---|
615 | // should only pass a received Ethernet frame to the framework for |
---|
616 | // further processing if it is both a Start of Frame and End of Frame. |
---|
617 | |
---|
618 | peripheral_args_t* peripheral_args = (peripheral_args_t*)callback_arg; |
---|
619 | eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance); |
---|
620 | |
---|
621 | rx_ring_ptr = XAxiDma_GetRxRing(eth_dma_instance_ptr); |
---|
622 | irq_status = XAxiDma_BdRingGetIrq(rx_ring_ptr); |
---|
623 | |
---|
624 | if (!(irq_status & XAXIDMA_IRQ_ERROR_MASK)) { |
---|
625 | // At least one reception is completed |
---|
626 | |
---|
627 | // Disable the interrupt and then acknowledge the interrupt |
---|
628 | XAxiDma_BdRingIntDisable(rx_ring_ptr, irq_status); |
---|
629 | XAxiDma_BdRingAckIrq(rx_ring_ptr, irq_status); |
---|
630 | |
---|
631 | // Get all the BDs that are available |
---|
632 | // NOTE: Use global variable so it is easy to process a sub-set of |
---|
633 | // packets at a time. |
---|
634 | bd_set_count = XAxiDma_BdRingFromHw(rx_ring_ptr, XAXIDMA_ALL_BDS, &bd_set_to_process_ptr); |
---|
635 | |
---|
636 | if(bd_set_count > 0) { |
---|
637 | while (bd_set_count > 0) { |
---|
638 | // Lookup length and data pointers from the DMA metadata |
---|
639 | eth_rx_len = XAxiDma_BdGetActualLength(bd_set_to_process_ptr, rx_ring_ptr->MaxTransferLen); |
---|
640 | eth_rx_buf = XAxiDma_BdGetBufAddr(bd_set_to_process_ptr); |
---|
641 | |
---|
642 | // Get the Rx Eth queue entry pointer. This is located further back in the queue_buffer_t struct. |
---|
643 | // |
---|
644 | eth_rx_queue_buffer = (eth_rx_queue_buffer_t*)( (u8*)eth_rx_buf - offsetof(eth_rx_queue_buffer_t, pkt)); |
---|
645 | eth_rx_queue_buffer->length = eth_rx_len; |
---|
646 | |
---|
647 | // Check and make sure this is both the start and end of a frame. |
---|
648 | // A large MTU reception could be split among multiple BDs. We do |
---|
649 | // not currently support jumbo receptions. |
---|
650 | // FIXME: we need similar code in the emac ps peripheral. |
---|
651 | if ( ((XAxiDma_BdGetSts(bd_set_to_process_ptr) & XAXIDMA_BD_STS_RXSOF_MASK) == 0) || |
---|
652 | ((XAxiDma_BdGetSts(bd_set_to_process_ptr) & XAXIDMA_BD_STS_RXEOF_MASK) == 0) ){ |
---|
653 | queue_checkin(eth_rx_queue_buffer->pyld_queue_hdr.dle); |
---|
654 | } else { |
---|
655 | // Add the Rx Eth queue entry to the list of packets to be processed by the High Framework |
---|
656 | if( peripheral_args->callback0(eth_rx_queue_buffer) == 0 ){ |
---|
657 | queue_checkin(eth_rx_queue_buffer->pyld_queue_hdr.dle); |
---|
658 | } |
---|
659 | } |
---|
660 | |
---|
661 | // Free the ETH DMA buffer descriptor |
---|
662 | status = XAxiDma_BdRingFree(rx_ring_ptr, 1, bd_set_to_process_ptr); |
---|
663 | if(status != XST_SUCCESS) { |
---|
664 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in XAxiDma_BdRingFree of Rx BD! Err = %d\n", status); |
---|
665 | } |
---|
666 | |
---|
667 | bd_set_count--; |
---|
668 | |
---|
669 | // Update to the next BD in the chain for the next iteration |
---|
670 | bd_set_to_process_ptr = XAxiDma_BdRingNext(rx_ring_ptr, bd_set_to_process_ptr); |
---|
671 | } |
---|
672 | |
---|
673 | // At this point, we heave dealt with every BD in the ring |
---|
674 | |
---|
675 | // Attach any newly free BDs |
---|
676 | wlan_axi_eth_intr_attach_free_rx_bd(peripheral_args); |
---|
677 | } |
---|
678 | |
---|
679 | // Finished all available Eth Rx - re-enable interrupt |
---|
680 | XAxiDma_BdRingIntEnable(rx_ring_ptr, irq_status); |
---|
681 | |
---|
682 | } else { |
---|
683 | // Acknowledge the error interrupt |
---|
684 | XAxiDma_BdRingAckIrq(rx_ring_ptr, XAXIDMA_IRQ_ERROR_MASK); |
---|
685 | // TODO: Clean up from error condition !!! |
---|
686 | } |
---|
687 | |
---|
688 | return; |
---|
689 | } |
---|
690 | |
---|
691 | /** |
---|
692 | * @brief Free any complete Tx BDs |
---|
693 | * |
---|
694 | * @param peripheral_args_t* peripheral_args |
---|
695 | * - necessary arguments to distinguish between multiple instances of this peripheral |
---|
696 | * @return none |
---|
697 | */ |
---|
698 | inline void _wlan_axi_eth_intr_free_tx_bd(peripheral_args_t* peripheral_args) { |
---|
699 | int i; |
---|
700 | int status; |
---|
701 | |
---|
702 | XAxiDma* eth_dma_instance_ptr; |
---|
703 | eth_tx_queue_buffer_t* eth_tx_queue_buffer; |
---|
704 | XAxiDma_BdRing* eth_tx_ring_ptr; |
---|
705 | u32 eth_tx_buf; |
---|
706 | |
---|
707 | int processed_bd_count; |
---|
708 | |
---|
709 | XAxiDma_Bd* bd_set_ptr = NULL; |
---|
710 | XAxiDma_Bd* bd_ptr = NULL; |
---|
711 | |
---|
712 | eth_dma_instance_ptr = (XAxiDma*)(peripheral_args->driver_instance); |
---|
713 | eth_tx_ring_ptr = XAxiDma_GetTxRing(eth_dma_instance_ptr); |
---|
714 | |
---|
715 | // Check how many buffer descriptors have been processed |
---|
716 | processed_bd_count = XAxiDma_BdRingFromHw(eth_tx_ring_ptr, XAXIDMA_ALL_BDS, &bd_set_ptr); |
---|
717 | |
---|
718 | // Initialize the working descriptor pointer |
---|
719 | bd_ptr = bd_set_ptr; |
---|
720 | |
---|
721 | for (i = 0; i < processed_bd_count; i++) { |
---|
722 | |
---|
723 | // Get the status of the descriptor |
---|
724 | status = XAxiDma_BdGetSts(bd_ptr); |
---|
725 | |
---|
726 | // Check the status |
---|
727 | if ((status & XAXIDMA_BD_STS_ALL_ERR_MASK) || (!(status & XAXIDMA_BD_STS_COMPLETE_MASK))) { |
---|
728 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in _wlan_axi_eth_poll_free_tx_bd(): Tx BD contained error status\n"); |
---|
729 | } |
---|
730 | |
---|
731 | if(XAxiDma_BdGetCtrl(bd_ptr) & XAXIDMA_BD_CTRL_TXSOF_MASK){ |
---|
732 | // This is the start of a frame, i.e. it is seg0 of a queue buffer. |
---|
733 | |
---|
734 | // Call the platform's Tx Done callback so it can clean up |
---|
735 | eth_tx_buf = XAxiDma_BdGetBufAddr(bd_ptr); |
---|
736 | |
---|
737 | // Get the Rx Eth queue entry pointer. This is located further back in the queue_buffer_t struct |
---|
738 | eth_tx_queue_buffer = (eth_tx_queue_buffer_t*)( (u8*)eth_tx_buf - offsetof(eth_tx_queue_buffer_t, seg0)); |
---|
739 | |
---|
740 | // Per the documentation for XAxiDMA_BdRingFromHw, "If hardware has partially |
---|
741 | // completed a packet spanning multiple BDs, then none of the BDs for that |
---|
742 | // packet will be included in the results." So, if we see the BD marked as a |
---|
743 | // start of frame, we know the entire frame must be complete. So, we can call |
---|
744 | // the _done_callback() function even if we have not finished looping through |
---|
745 | // every BD. |
---|
746 | peripheral_args->callback1(eth_tx_queue_buffer); |
---|
747 | } |
---|
748 | |
---|
749 | // Get next descriptor |
---|
750 | bd_ptr = (XAxiDma_Bd*)XAxiDma_BdRingNext(eth_tx_ring_ptr, bd_ptr); |
---|
751 | } |
---|
752 | |
---|
753 | |
---|
754 | // Free all processed RX BDs for future transmission |
---|
755 | status = XAxiDma_BdRingFree(eth_tx_ring_ptr, processed_bd_count, bd_set_ptr); |
---|
756 | |
---|
757 | if (status != XST_SUCCESS) { |
---|
758 | xil_printf("WLAN_AXI_ETHERNET_INTR: Error in _wlan_axi_eth_poll_free_tx_bd(): Unable to free Tx ring"); |
---|
759 | return; |
---|
760 | } |
---|
761 | return; |
---|
762 | } |
---|
763 | |
---|
764 | |
---|