1 | /* |
---|
2 | * network.c |
---|
3 | * RTEMS_490 |
---|
4 | * |
---|
5 | * Created by Michael Hamel on 18/11/08. |
---|
6 | * |
---|
7 | * This code is for the PPC405EX, 405EXr on the Haleakala board with an |
---|
8 | * 88E1111 PHY. |
---|
9 | * Its has some adaptations for the 405GP, and 405GPr (untested). |
---|
10 | * |
---|
11 | * It should handle dual interfaces correctly, but has not been tested. |
---|
12 | * |
---|
13 | */ |
---|
14 | |
---|
15 | #define __INSIDE_RTEMS_BSD_TCPIP_STACK__ |
---|
16 | |
---|
17 | #include <bsp.h> |
---|
18 | #include <stdio.h> |
---|
19 | #include <errno.h> |
---|
20 | #include <rtems/error.h> |
---|
21 | #include <rtems/rtems_bsdnet.h> |
---|
22 | #include <rtems/rtems_mii_ioctl.h> |
---|
23 | #include <rtems/score/assert.h> |
---|
24 | |
---|
25 | #include <sys/param.h> |
---|
26 | #include <sys/mbuf.h> |
---|
27 | #include <sys/socket.h> |
---|
28 | #include <sys/sockio.h> |
---|
29 | |
---|
30 | #include <net/if.h> |
---|
31 | |
---|
32 | #include <netinet/in.h> |
---|
33 | |
---|
34 | #include <netinet/if_ether.h> |
---|
35 | #include <bsp/irq.h> |
---|
36 | |
---|
37 | #include <sys/types.h> |
---|
38 | #include <sys/socket.h> |
---|
39 | |
---|
40 | #include <ppc4xx/ppc405gp.h> |
---|
41 | #include <ppc4xx/ppc405ex.h> |
---|
42 | |
---|
43 | #define qDebug /* General printf debugging */ |
---|
44 | /* #define qMultiDebug */ /* Debugging for the multicast hardware filter */ |
---|
45 | |
---|
46 | /*---------------------------- Hardware definitions -------------------------- */ |
---|
47 | |
---|
48 | /* PHY addresses for Kilauea & Haleakala; defined by hardware */ |
---|
49 | enum { |
---|
50 | kPHY0 = 1, |
---|
51 | kPHY1 = 2, |
---|
52 | kMaxEMACs = 2 |
---|
53 | }; |
---|
54 | |
---|
55 | enum { |
---|
56 | kMaxRxBuffers = 256, |
---|
57 | kNXmtDescriptors = 256, /* May as well use all of them */ |
---|
58 | kNoXmtBusy = 666 /* Arbitrary flag value outside 0..kNXmtDescriptors */ |
---|
59 | }; |
---|
60 | |
---|
61 | |
---|
62 | /*----------------------- Local variables for the driver -------------------------- */ |
---|
63 | |
---|
64 | typedef struct MALDescriptor { |
---|
65 | uint16_t ctrlBits; |
---|
66 | uint16_t adrDataSize; /* 4 bits of high address, 12 bits of length */ |
---|
67 | uint8_t* ptr; |
---|
68 | } MALDescriptor; |
---|
69 | |
---|
70 | typedef struct EMACLocals { |
---|
71 | struct arpcom arpcom; |
---|
72 | |
---|
73 | /* Pointer to memory-mapped hardware */ |
---|
74 | volatile EthernetRegisters_GP* EMAC; |
---|
75 | |
---|
76 | /* Transmit and receive task references */ |
---|
77 | rtems_id rxDaemonTid; |
---|
78 | rtems_id txDaemonTid; |
---|
79 | int nRxBuffers; |
---|
80 | int xmtFreeIndex; |
---|
81 | int xmtBusyIndex; |
---|
82 | MALDescriptor* xmtDescTable; |
---|
83 | MALDescriptor* rcvDescTable; |
---|
84 | |
---|
85 | struct mbuf* rxMBufs[kMaxRxBuffers]; |
---|
86 | struct mbuf* txMBufs[kNXmtDescriptors]; |
---|
87 | |
---|
88 | int phyAddr, /* PHY address */ |
---|
89 | phyState, /* Last link state */ |
---|
90 | phyOUI, /* Cached PHY type info */ |
---|
91 | phyModel, |
---|
92 | phyRev; |
---|
93 | |
---|
94 | /* Statistics */ |
---|
95 | uint32_t rxInterrupts; |
---|
96 | uint32_t rxOverrun; |
---|
97 | uint32_t rxRunt; |
---|
98 | uint32_t rxBadCRC; |
---|
99 | uint32_t rxNonOctet; |
---|
100 | uint32_t rxGiant; |
---|
101 | |
---|
102 | uint32_t txInterrupts; |
---|
103 | |
---|
104 | uint32_t txLostCarrier; |
---|
105 | uint32_t txDeferred; |
---|
106 | uint32_t txOneCollision; |
---|
107 | uint32_t txMultiCollision; |
---|
108 | uint32_t txTooManyCollision; |
---|
109 | uint32_t txLateCollision; |
---|
110 | uint32_t txUnderrun; |
---|
111 | uint32_t txPoorSignal; |
---|
112 | } EMACLocals; |
---|
113 | |
---|
114 | |
---|
115 | EMACLocals gEmacs[kMaxEMACs]; |
---|
116 | |
---|
117 | int ppc405_emac_phy_adapt(EMACLocals* ep); |
---|
118 | |
---|
119 | /*----------------------------------- Globals --------------------------------------*/ |
---|
120 | |
---|
121 | /* |
---|
122 | Pointers to the buffer descriptor tables used by the MAL. Tricky because they are both |
---|
123 | read and written to by the MAL, which is unaware of the CPU data cache. As four 8-byte |
---|
124 | descriptors fit into a single cache line this makes managing them in cached memory difficult. |
---|
125 | Best solution is to label them as uncached using the MMU. This code assumes an appropriate |
---|
126 | sized block stating at _enet_bdesc_base has been reserved by linkcmds and has been set up |
---|
127 | with uncached MMU attrributes in bspstart.c */ |
---|
128 | |
---|
129 | LINKER_SYMBOL(_enet_bdesc_start); /* start of buffer descriptor space, from linkcmds */ |
---|
130 | LINKER_SYMBOL(_enet_bdesc_end); /* top limit, from linkcmds */ |
---|
131 | |
---|
132 | static MALDescriptor* gTx0Descs = NULL; |
---|
133 | static MALDescriptor* gRx0Descs = NULL; |
---|
134 | static MALDescriptor* gTx1Descs = NULL; |
---|
135 | static MALDescriptor* gRx1Descs = NULL; |
---|
136 | |
---|
137 | /*------------------------------------------------------------*/ |
---|
138 | |
---|
139 | |
---|
140 | /* |
---|
141 | * RTEMS event used by interrupt handler to signal driver tasks. |
---|
142 | * This must not be any of the events used by the network task synchronization. |
---|
143 | */ |
---|
144 | #define INTERRUPT_EVENT RTEMS_EVENT_1 |
---|
145 | |
---|
146 | /* |
---|
147 | * RTEMS event used to start transmit daemon. |
---|
148 | * This must not be the same as INTERRUPT_EVENT. |
---|
149 | */ |
---|
150 | #define START_TRANSMIT_EVENT RTEMS_EVENT_2 |
---|
151 | |
---|
152 | #define _sync __asm__ volatile ("sync\n"::) |
---|
153 | |
---|
154 | #define kCacheLineMask (PPC_CACHE_ALIGNMENT - 1) |
---|
155 | |
---|
156 | |
---|
157 | /*----------------------- IRQ handler glue -----------------------*/ |
---|
158 | |
---|
159 | static void InstallIRQHandler(rtems_irq_number id, |
---|
160 | rtems_irq_hdl handler, |
---|
161 | rtems_irq_enable turnOn, |
---|
162 | rtems_irq_disable turnOff) |
---|
163 | { |
---|
164 | rtems_irq_connect_data params; |
---|
165 | |
---|
166 | params.name = id; |
---|
167 | params.hdl = handler; |
---|
168 | params.on = turnOn; |
---|
169 | params.off = turnOff; |
---|
170 | params.isOn = NULL; |
---|
171 | params.handle = NULL; |
---|
172 | if (! BSP_install_rtems_irq_handler(¶ms)) |
---|
173 | rtems_panic ("Can't install interrupt handler"); |
---|
174 | } |
---|
175 | |
---|
176 | static void |
---|
177 | NoAction(const rtems_irq_connect_data* unused) |
---|
178 | { |
---|
179 | /* printf("NoAction %d\n",unused->name); */ |
---|
180 | } |
---|
181 | |
---|
182 | |
---|
183 | /*------------------------ PHY interface -------------------------- */ |
---|
184 | /* This code recognises and works with the 88E1111 only. Other PHYs |
---|
185 | will require appropriate adaptations to this code */ |
---|
186 | |
---|
187 | enum { |
---|
188 | kPHYControl = 0, |
---|
189 | kPHYReset = 0x8000, |
---|
190 | kPHYStatus = 1, |
---|
191 | kPHYLinkStatus = 0x0004, |
---|
192 | kPHYID1 = 2, |
---|
193 | kPHYID2 = 3, |
---|
194 | kPHYAutoNegExp = 6, |
---|
195 | kPHY1000BaseTCtl = 9, |
---|
196 | kPHYExtStatus = 15, |
---|
197 | |
---|
198 | /* 88E1111 identification */ |
---|
199 | kMarvellOUI = 0x5043, |
---|
200 | k88E1111Part = 0x0C, |
---|
201 | |
---|
202 | /* 88E1111 register addresses */ |
---|
203 | k8PHYSpecStatus = 17, |
---|
204 | k8PHYSpeedShift = 14, |
---|
205 | k8PHYDuplex = 0x2000, |
---|
206 | k8PHYResolved = 0x0800, |
---|
207 | k8PHYLinkUp = 0x0400, |
---|
208 | k8IntStatus = 19, |
---|
209 | k8IntEnable = 18, |
---|
210 | k8AutoNegComplete = 0x0800, |
---|
211 | k8LinkStateChanged = 0x0400, |
---|
212 | k8ExtCtlReg = 20, |
---|
213 | k8RcvTimingDelay = 0x0080, |
---|
214 | k8XmtTimingDelay = 0x0002, |
---|
215 | k8XmtEnable = 0x0001, |
---|
216 | k8LEDCtlReg = 24, |
---|
217 | k8ExtStatusReg = 27, |
---|
218 | }; |
---|
219 | |
---|
220 | |
---|
221 | static uint16_t ReadPHY(EMACLocals* ep, uint8_t reg) |
---|
222 | { |
---|
223 | int n = 0; |
---|
224 | uint32_t t; |
---|
225 | |
---|
226 | reg &= 0x1F; |
---|
227 | |
---|
228 | /* 405EX-specific! */ |
---|
229 | while ((ep->EMAC->STAcontrol & keSTARun) != 0) |
---|
230 | { ; } |
---|
231 | ep->EMAC->STAcontrol = keSTADirectRd + (ep->phyAddr<<5) + reg; |
---|
232 | ep->EMAC->STAcontrol |= keSTARun; |
---|
233 | /* Wait for the read to complete, should take ~25usec */ |
---|
234 | do { |
---|
235 | t = ep->EMAC->STAcontrol; |
---|
236 | if (++n > 200000) |
---|
237 | rtems_panic("PHY read timed out"); |
---|
238 | } while ((t & keSTARun) != 0); |
---|
239 | |
---|
240 | if (t & kSTAErr) |
---|
241 | rtems_panic("PHY read failed"); |
---|
242 | return t >> 16; |
---|
243 | } |
---|
244 | |
---|
245 | static void WritePHY(EMACLocals* ep, uint8_t reg, uint16_t value) |
---|
246 | { |
---|
247 | |
---|
248 | reg &= 0x1F; |
---|
249 | |
---|
250 | /* 405EX-specific */ |
---|
251 | while ((ep->EMAC->STAcontrol & keSTARun) != 0) |
---|
252 | { ; } |
---|
253 | ep->EMAC->STAcontrol = (value<<16) | keSTADirectWr | (ep->phyAddr<<5) | reg; |
---|
254 | ep->EMAC->STAcontrol |= keSTARun; |
---|
255 | } |
---|
256 | |
---|
257 | static void ResetPHY(EMACLocals* ep) |
---|
258 | { |
---|
259 | int n; |
---|
260 | |
---|
261 | n = ReadPHY(ep, kPHYControl); |
---|
262 | n |= kPHYReset; |
---|
263 | WritePHY(ep, kPHYControl, n); |
---|
264 | do { |
---|
265 | rtems_task_wake_after( (rtems_bsdnet_ticks_per_second/20) + 1 ); |
---|
266 | n = ReadPHY(ep, kPHYControl); |
---|
267 | } while ((n & kPHYReset)!=0); |
---|
268 | } |
---|
269 | |
---|
270 | enum { |
---|
271 | kELinkUp = 0x80, |
---|
272 | kELinkFullDuplex = 0x40, |
---|
273 | kELinkSpeed10 = 0, |
---|
274 | kELinkSpeed100 = 1, |
---|
275 | kELinkSpeed1000 = 2, |
---|
276 | kELinkSpeedMask = 3 |
---|
277 | }; |
---|
278 | |
---|
279 | static int GetPHYLinkState(EMACLocals* ep) |
---|
280 | /* Return link state (up/speed/duplex) as a set of flags */ |
---|
281 | { |
---|
282 | int state, result; |
---|
283 | |
---|
284 | /* if (ep->phyOUI==kMarvellOUI) */ |
---|
285 | result = 0; |
---|
286 | state = ReadPHY(ep,k8PHYSpecStatus); |
---|
287 | if ((state & k8PHYLinkUp) && (state & k8PHYResolved)) { |
---|
288 | result |= kELinkUp; |
---|
289 | if (state & k8PHYDuplex) result |= kELinkFullDuplex; |
---|
290 | result |= ((state >> k8PHYSpeedShift) & 3); |
---|
291 | } |
---|
292 | return result; |
---|
293 | } |
---|
294 | |
---|
295 | /*---------------------- PHY setup ------------------------*/ |
---|
296 | |
---|
297 | |
---|
298 | static void InitPHY(EMACLocals* ep) |
---|
299 | { |
---|
300 | int id,id2,n; |
---|
301 | |
---|
302 | id = ReadPHY(ep,kPHYID1); |
---|
303 | id2 = ReadPHY(ep,kPHYID2); |
---|
304 | ep->phyOUI = (id<<6) + (id2>>10); |
---|
305 | ep->phyModel = (id2>>4) & 0x1F; |
---|
306 | ep->phyRev = id2 & 0xF; |
---|
307 | |
---|
308 | #ifdef qDebug |
---|
309 | printf("PHY %d maker $%X model %d revision %d\n",ep->phyAddr,ep->phyOUI,ep->phyModel,ep->phyRev); |
---|
310 | #endif |
---|
311 | |
---|
312 | /* Test for PHYs that we understand; insert new PHY types initialisation here */ |
---|
313 | if (ep->phyOUI == kMarvellOUI || ep->phyModel == k88E1111Part) { |
---|
314 | /* 88E111-specific: Enable RxTx timing control, enable transmitter */ |
---|
315 | n = ReadPHY(ep, k8ExtCtlReg); |
---|
316 | n |= k8RcvTimingDelay + k8XmtTimingDelay + k8XmtEnable; |
---|
317 | WritePHY(ep, k8ExtCtlReg, n); |
---|
318 | |
---|
319 | /* Set LED mode; Haleakala has LINK10 and TX LEDs only. Set up to do 100/1000 and link up/active*/ |
---|
320 | WritePHY(ep, k8LEDCtlReg, 0x4109); |
---|
321 | |
---|
322 | /* Need to do a reset after fiddling with registers*/ |
---|
323 | ResetPHY(ep); |
---|
324 | } else |
---|
325 | rtems_panic("Unknown PHY type"); |
---|
326 | } |
---|
327 | |
---|
328 | |
---|
329 | /*--------------------- Interrupt handlers for the MAL ----------------------------- */ |
---|
330 | |
---|
331 | static void |
---|
332 | MALTXDone_handler(rtems_irq_hdl_param param) |
---|
333 | { |
---|
334 | int n; |
---|
335 | |
---|
336 | n = PPC_DEVICE_CONTROL_REGISTER(MAL0_TXEOBISR); |
---|
337 | if (n & kMALChannel0) { |
---|
338 | gEmacs[0].txInterrupts++; |
---|
339 | rtems_event_send (gEmacs[0].txDaemonTid, INTERRUPT_EVENT); |
---|
340 | } |
---|
341 | if (n & kMALChannel1) { |
---|
342 | gEmacs[1].txInterrupts++; |
---|
343 | rtems_event_send (gEmacs[1].txDaemonTid, INTERRUPT_EVENT); |
---|
344 | } |
---|
345 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXEOBISR,n); |
---|
346 | } |
---|
347 | |
---|
348 | static void |
---|
349 | MALRXDone_handler (rtems_irq_hdl_param param) |
---|
350 | { |
---|
351 | int n; |
---|
352 | |
---|
353 | n = PPC_DEVICE_CONTROL_REGISTER(MAL0_RXEOBISR); |
---|
354 | if (n & kMALChannel0) { |
---|
355 | gEmacs[0].rxInterrupts++; |
---|
356 | rtems_event_send (gEmacs[0].rxDaemonTid, INTERRUPT_EVENT); |
---|
357 | } |
---|
358 | if (n & kMALChannel1) { |
---|
359 | gEmacs[1].rxInterrupts++; |
---|
360 | rtems_event_send (gEmacs[1].rxDaemonTid, INTERRUPT_EVENT); |
---|
361 | } |
---|
362 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXEOBISR,n); /* Write back to clear interrupt */ |
---|
363 | } |
---|
364 | |
---|
365 | /* These handlers are useful for debugging, but we don't actually need to process these interrupts */ |
---|
366 | |
---|
367 | static void |
---|
368 | MALErr_handler (rtems_irq_hdl_param param) |
---|
369 | { |
---|
370 | uint32_t errCause; |
---|
371 | |
---|
372 | errCause = PPC_DEVICE_CONTROL_REGISTER(MAL0_ESR); |
---|
373 | /* Clear the error */ |
---|
374 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_ESR,errCause); |
---|
375 | } |
---|
376 | |
---|
377 | static void |
---|
378 | EMAC0Err_handler (rtems_irq_hdl_param param) |
---|
379 | { |
---|
380 | uint32_t errCause; |
---|
381 | |
---|
382 | errCause = gEmacs[0].EMAC->intStatus; |
---|
383 | /* Clear error by writing back */ |
---|
384 | gEmacs[0].EMAC->intStatus = errCause; |
---|
385 | } |
---|
386 | |
---|
387 | static void |
---|
388 | EMAC1Err_handler (rtems_irq_hdl_param param) |
---|
389 | { |
---|
390 | uint32_t errCause; |
---|
391 | |
---|
392 | errCause = gEmacs[1].EMAC->intStatus; |
---|
393 | /* Clear error by writing back */ |
---|
394 | gEmacs[1].EMAC->intStatus = errCause; |
---|
395 | } |
---|
396 | |
---|
397 | |
---|
398 | /*--------------------- Low-level hardware initialisation ----------------------------- */ |
---|
399 | |
---|
400 | |
---|
401 | |
---|
402 | static void |
---|
403 | mal_initialise(void) |
---|
404 | { |
---|
405 | uint32_t bdescbase; |
---|
406 | int nBytes, ntables; |
---|
407 | |
---|
408 | /*------------------- Initialise the MAL for both channels ---------------------- */ |
---|
409 | |
---|
410 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_CFG,kMALReset); |
---|
411 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCARR, kMALChannel0 | kMALChannel1); |
---|
412 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCARR, kMALChannel0 | kMALChannel1); |
---|
413 | |
---|
414 | /* Acquire MAL interrupts */ |
---|
415 | InstallIRQHandler(BSP_UIC_MALTXEOB, MALTXDone_handler, NoAction, NoAction); |
---|
416 | InstallIRQHandler(BSP_UIC_MALRXEOB, MALRXDone_handler, NoAction, NoAction); |
---|
417 | InstallIRQHandler(BSP_UIC_MALSERR, MALErr_handler, NoAction, NoAction); |
---|
418 | |
---|
419 | /* Set up the buffer descriptor tables */ |
---|
420 | bdescbase = (uint32_t)(_enet_bdesc_start); |
---|
421 | nBytes = sizeof(MALDescriptor) * 256; |
---|
422 | ntables = 4; |
---|
423 | if (get_ppc_cpu_type() != PPC_405EX) { |
---|
424 | /* The 405GP/GPr requires table bases to be 4K-aligned and can use two tx channels on one EMAC */ |
---|
425 | nBytes = (nBytes + 0x0FFF) & ~0x0FFF; |
---|
426 | bdescbase = (bdescbase + 0x0FFF) & ~0x0FFF; |
---|
427 | ntables = 3; |
---|
428 | } |
---|
429 | |
---|
430 | /* printf("Buffer descriptors at $%X..$%X, code from $%X\n",bdescbase, bdescbase + nBytes*ntables - 1,(uint32_t)&_text_start); */ |
---|
431 | |
---|
432 | /* Check that we have been given enough space and the buffers don't run past the enet_bdesc_end address */ |
---|
433 | if (bdescbase + nBytes*ntables > (uint32_t)_enet_bdesc_end) |
---|
434 | rtems_panic("Ethernet descriptor space insufficient!"); |
---|
435 | |
---|
436 | gTx0Descs = (MALDescriptor*)bdescbase; |
---|
437 | gTx1Descs = (MALDescriptor*)(bdescbase + nBytes); |
---|
438 | gRx0Descs = (MALDescriptor*)(bdescbase + nBytes*2); |
---|
439 | /* Clear the buffer descriptor tables */ |
---|
440 | memset(gTx0Descs, 0, sizeof(MALDescriptor)*256); |
---|
441 | memset(gTx1Descs, 0, sizeof(MALDescriptor)*256); |
---|
442 | memset(gRx0Descs, 0, sizeof(MALDescriptor)*256); |
---|
443 | if (get_ppc_cpu_type() == PPC_405EX) { |
---|
444 | gRx1Descs = (MALDescriptor*)(bdescbase + nBytes*3); |
---|
445 | memset(gRx1Descs, 0, sizeof(MALDescriptor)*256); |
---|
446 | } |
---|
447 | |
---|
448 | /* Set up the MAL registers */ |
---|
449 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCTP0R,gTx0Descs); |
---|
450 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCTP1R,gTx1Descs); |
---|
451 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCTP0R,gRx0Descs); |
---|
452 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RCBS0, (MCLBYTES-16)>>4); /* The hardware writes directly to the mbuf clusters, so it can write MCLBYTES */ |
---|
453 | if (get_ppc_cpu_type() == PPC_405EX) { |
---|
454 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_CFG,kMALMedHiPriority + keMALRdMaxBurst32 + keMALWrMedHiPriority + keMALWrMaxBurst32 + |
---|
455 | kMALLocksOPB + kMALLocksErrs + kMALCanBurst); |
---|
456 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCTP1R,gRx1Descs); |
---|
457 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RCBS1, (MCLBYTES-16)>>4); |
---|
458 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_IER,0xF7); /* Enable all MAL interrupts */ |
---|
459 | } else { |
---|
460 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_CFG,kMALMedHiPriority + kMALLocksOPB + kMALLocksErrs + kMALCanBurst + kMALLatency8); |
---|
461 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_IER,0x1F); /* Enable all MAL interrupts */ |
---|
462 | } |
---|
463 | } |
---|
464 | |
---|
465 | |
---|
466 | |
---|
467 | #ifdef qDebug |
---|
468 | static void printaddr(uint8_t* enetaddr) |
---|
469 | { |
---|
470 | printf("%02X.%02X.%02X.%02X.%02X.%02X",enetaddr[0],enetaddr[1],enetaddr[2],enetaddr[3],enetaddr[4],enetaddr[5]); |
---|
471 | } |
---|
472 | #endif |
---|
473 | |
---|
474 | static bool gMALInited = FALSE; |
---|
475 | |
---|
476 | static void |
---|
477 | ppc405_emac_initialize_hardware(EMACLocals* ep) |
---|
478 | { |
---|
479 | |
---|
480 | int n,mfr; |
---|
481 | int unitnum = ep->arpcom.ac_if.if_unit; |
---|
482 | |
---|
483 | if (get_ppc_cpu_type() == PPC_405EX) { |
---|
484 | /* PPC405EX: configure the RGMII bridge and clocks */ |
---|
485 | RGMIIRegisters* rgmp = (RGMIIRegisters*)RGMIIAddress; |
---|
486 | rgmp->FER = 0x00080055; /* Both EMACs RGMII */ |
---|
487 | rgmp->SSR = 0x00000044; /* Both EMACs 1000Mbps */ |
---|
488 | /* Configure the TX clock to be external */ |
---|
489 | mfsdr(SDR0_MFR,mfr); |
---|
490 | mfr &= ~0x0C000000; /* Switches both PHYs */ |
---|
491 | mtsdr(SDR0_MFR,mfr); |
---|
492 | } |
---|
493 | |
---|
494 | /* Reset the EMAC */ |
---|
495 | n = 0; |
---|
496 | ep->EMAC->mode0 = kEMACSoftRst; |
---|
497 | while ((ep->EMAC->mode0 & kEMACSoftRst) != 0) |
---|
498 | n++; /* Wait for it to complete */ |
---|
499 | |
---|
500 | /* Set up so we can talk to the PHY */ |
---|
501 | ep->EMAC->mode1 = keEMACIPHYAddr4 | keEMACOPB100MHz; |
---|
502 | |
---|
503 | /* Initialise the PHY */ |
---|
504 | InitPHY(ep); |
---|
505 | |
---|
506 | /* Initialise the MAL (once only) */ |
---|
507 | if ( ! gMALInited) { |
---|
508 | mal_initialise(); |
---|
509 | gMALInited = TRUE; |
---|
510 | } |
---|
511 | |
---|
512 | /* Set up IRQ handlers and enable the MAL channels for this port */ |
---|
513 | if (unitnum==0) { |
---|
514 | ep->xmtDescTable = gTx0Descs; |
---|
515 | ep->rcvDescTable = gRx0Descs; |
---|
516 | InstallIRQHandler(BSP_UIC_EMAC0, EMAC0Err_handler, NoAction, NoAction); |
---|
517 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCASR,kMALChannel0); |
---|
518 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCASR,kMALChannel0); |
---|
519 | } else { |
---|
520 | ep->xmtDescTable = gTx1Descs; |
---|
521 | ep->rcvDescTable = gRx1Descs; |
---|
522 | InstallIRQHandler(BSP_UIC_EMAC1, EMAC1Err_handler, NoAction, NoAction); |
---|
523 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCASR,kMALChannel1); |
---|
524 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCASR,kMALChannel1); |
---|
525 | } |
---|
526 | |
---|
527 | /* The rest of the EMAC initialisation is done in emac_phy_adapt |
---|
528 | when we know what the characteristics of the link are */ |
---|
529 | } |
---|
530 | |
---|
531 | |
---|
532 | /* EMAC second stage initialisation; talks to the PHY to find out how to do it. |
---|
533 | Resets the EMAC if the PHY parameters need to be changed */ |
---|
534 | |
---|
535 | int ppc405_emac_phy_adapt(EMACLocals* ep) |
---|
536 | { |
---|
537 | int linkState = GetPHYLinkState(ep); |
---|
538 | int spd; |
---|
539 | |
---|
540 | if ((linkState & kELinkUp) && (linkState != ep->phyState)) { |
---|
541 | /* Reset the EMAC and set registers according to PHY state */ |
---|
542 | int i,n = 0; |
---|
543 | uint32_t mode, rmode; |
---|
544 | |
---|
545 | ep->EMAC->mode0 = kEMACSoftRst; |
---|
546 | while ((ep->EMAC->mode0 & kEMACSoftRst) != 0) |
---|
547 | n++; /* Wait for it to complete */ |
---|
548 | spd = linkState & kELinkSpeedMask; |
---|
549 | mode = (spd<<22) | kgEMACTx0Multi; |
---|
550 | if (get_ppc_cpu_type() == PPC_405EX) |
---|
551 | mode |= (keEMAC16KRxFIFO | keEMAC16KTxFIFO | keEMACIPHYAddr4 | keEMACOPB100MHz ); |
---|
552 | else |
---|
553 | mode |= (kgEMAC4KRxFIFO | kgEMAC2KTxFIFO); |
---|
554 | if (linkState & kELinkFullDuplex) |
---|
555 | mode |= kEMACFullDuplex + kEMACDoFlowControl; |
---|
556 | if ( (linkState & kELinkFullDuplex) || (spd > kELinkSpeed10) ) |
---|
557 | mode |= kEMACIgnoreSQE; |
---|
558 | |
---|
559 | |
---|
560 | if (spd==kELinkSpeed1000) { |
---|
561 | /* Gigabit, so we support jumbo frames. Take appropriate measures: adjust the if_mtu */ |
---|
562 | /* Note that we do this here because changing it later doesn't work very well : see |
---|
563 | the SIOCSIFMTU discussion below */ |
---|
564 | struct ifnet* ifp = &ep->arpcom.ac_if; |
---|
565 | ifp->if_mtu = ETHERMTU_JUMBO; |
---|
566 | mode |= keEMACJumbo; |
---|
567 | } |
---|
568 | |
---|
569 | |
---|
570 | ep->phyState = linkState; |
---|
571 | ep->EMAC->mode1 = mode; |
---|
572 | |
---|
573 | /* Install 48-bit hardware address that we have been given */ |
---|
574 | ep->EMAC->addrHi = (ep->arpcom.ac_enaddr[0]<<8) + ep->arpcom.ac_enaddr[1]; |
---|
575 | ep->EMAC->addrLo = (ep->arpcom.ac_enaddr[2]<<24) + (ep->arpcom.ac_enaddr[3]<<16) |
---|
576 | + (ep->arpcom.ac_enaddr[4]<<8) + (ep->arpcom.ac_enaddr[5] ); |
---|
577 | |
---|
578 | /* Set receive mode appropriately */ |
---|
579 | rmode = kEMACStripPadding + kEMACStripFCS + kEMACBrcastRcv; |
---|
580 | |
---|
581 | if (ep->arpcom.ac_if.if_flags & IFF_PROMISC) rmode |= kEMACPromiscRcv; |
---|
582 | else rmode |= kEMACIndivRcv; |
---|
583 | if (get_ppc_cpu_type() == PPC_405EX) |
---|
584 | rmode |= keEMACRxFIFOAFMax; |
---|
585 | if ((ep->arpcom.ac_if.if_flags & IFF_ALLMULTI) != 0) |
---|
586 | rmode |= kEMACPromMultRcv; |
---|
587 | else if ((ep->arpcom.ac_if.if_flags & IFF_MULTICAST) != 0) |
---|
588 | rmode |= kEMACMultcastRcv; |
---|
589 | |
---|
590 | ep->EMAC->rcvMode = rmode; |
---|
591 | |
---|
592 | if (get_ppc_cpu_type() == PPC_405EX) |
---|
593 | for (i=0; i<8; i++) |
---|
594 | ep->EMAC->e_groupHash[i] = 0; |
---|
595 | else |
---|
596 | for (i=0; i<4; i++) |
---|
597 | ep->EMAC->g_groupHash[i] = 0; |
---|
598 | |
---|
599 | if (get_ppc_cpu_type() == PPC_405EX) { |
---|
600 | /* Rcv low watermark, must be < mode1 Rcv FIFO size and > MAL burst length (default 64x4 bytes), 16-byte units |
---|
601 | High watermark must be > low and < RcvFIFO size */ |
---|
602 | ep->EMAC->rcvWatermarks = (16<<22) + (768<<6); |
---|
603 | /* Xmt low request must be >= 17 FIFO entries, Xmt urgent must be > low */ |
---|
604 | ep->EMAC->xmtMode1 = (17<<27) + (68<<14); /* TLR = 17, TUR = 68 */ |
---|
605 | /* Xmt partial packet request threshold */ |
---|
606 | ep->EMAC->xmtReqThreshold = ((1000>>2)-1) << 24; /* TRTR[TRT] = 1000 FIFO entries */ |
---|
607 | } else { |
---|
608 | ep->EMAC->rcvWatermarks = (15<<24) + (32<<8); |
---|
609 | ep->EMAC->xmtReqThreshold = ((1448>>6)-1) << 26; /* TRT = 1024b */ |
---|
610 | ep->EMAC->xmtMode1 = 0x40200000; /* TLR = 8w=32b, TUR=32w=128b */ |
---|
611 | } |
---|
612 | |
---|
613 | ep->EMAC->IPGap = 8; |
---|
614 | |
---|
615 | /* Want EMAC interrupts for error logging & statistics */ |
---|
616 | ep->EMAC->intEnable = kEMACIOverrun + kEMACIPause + kEMACIBadPkt + kEMACIRuntPkt + kEMACIShortEvt |
---|
617 | + kEMACIAlignErr + kEMACIBadFCS + kEMACIOverSize + kEMACILLCRange + kEMACISQEErr |
---|
618 | + kEMACITxErr; |
---|
619 | |
---|
620 | /* Start it running */ |
---|
621 | ep->EMAC->mode0 = kEMACRxEnable + kEMACTxEnable; |
---|
622 | return 0; |
---|
623 | } else |
---|
624 | return -1; |
---|
625 | } |
---|
626 | |
---|
627 | |
---|
628 | static void |
---|
629 | ppc405_emac_disable(EMACLocals* ep) |
---|
630 | /* Disable the EMAC channels so we stop running and processing interrupts */ |
---|
631 | { |
---|
632 | ep->EMAC->mode0 = 0; |
---|
633 | } |
---|
634 | |
---|
635 | static void |
---|
636 | ppc405_emac_startxmt(EMACLocals* ep) |
---|
637 | /* Start the transmitter: set TMR0[GNP] */ |
---|
638 | { |
---|
639 | ep->EMAC->xmtMode0 = kEMACNewPacket0 + 7; /* *** TFAE value for EX */ |
---|
640 | } |
---|
641 | |
---|
642 | static void ppc405_emac_watchdog(struct ifnet* ifp) |
---|
643 | /* Called if a transmit stalls or when the interface is down. Check the PHY |
---|
644 | until we get a valid link */ |
---|
645 | { |
---|
646 | EMACLocals* ep = ifp->if_softc; |
---|
647 | |
---|
648 | if (ppc405_emac_phy_adapt(ep)==0) { |
---|
649 | ep->arpcom.ac_if.if_flags |= IFF_RUNNING; |
---|
650 | ifp->if_timer = 0; /* No longer needed */ |
---|
651 | } else |
---|
652 | ifp->if_timer = 1; /* reschedule, once a second */ |
---|
653 | } |
---|
654 | |
---|
655 | |
---|
656 | |
---|
657 | /*----------------------- The transmit daemon/task -------------------------- */ |
---|
658 | |
---|
659 | |
---|
660 | static void |
---|
661 | FreeTxDescriptors(EMACLocals* ep) |
---|
662 | /* Make descriptors and mbufs at xmtBusyIndex available for re-use if the packet that they */ |
---|
663 | /* point at has all gone */ |
---|
664 | { |
---|
665 | uint16_t scan, status; |
---|
666 | |
---|
667 | if (ep->xmtBusyIndex != kNoXmtBusy) { |
---|
668 | scan = ep->xmtBusyIndex; |
---|
669 | while (TRUE) { |
---|
670 | /* Scan forward through the descriptors */ |
---|
671 | status = ep->xmtDescTable[scan].ctrlBits; |
---|
672 | if (++scan >= kNXmtDescriptors) |
---|
673 | scan = 0; |
---|
674 | /* If we find a ready (i.e not-yet-sent) descriptor, stop */ |
---|
675 | if ((status & kMALTxReady) != 0) |
---|
676 | break; |
---|
677 | /* If we find a last descriptor, we can free all the buffers up to and including it */ |
---|
678 | if ((status & kMALLast) != 0) { |
---|
679 | /* End of packet and it has been sent or abandoned; advance xmtBusyIndex to */ |
---|
680 | /* the next buffer and free buffers. */ |
---|
681 | if ((status & kEMACErrMask) != 0) { |
---|
682 | /* Transmit error of some kind */ |
---|
683 | |
---|
684 | if ((status & kEMACDeferred) != 0) |
---|
685 | ep->txDeferred++; |
---|
686 | if ((status & kEMACLostCarrier) != 0) |
---|
687 | ep->txLostCarrier++; /* *** Perhaps more serious reaction needed... */ |
---|
688 | |
---|
689 | if ((status & kEMACLateColl) != 0) |
---|
690 | ep->txLateCollision++; |
---|
691 | if ((status & kEMACOneColl) != 0) |
---|
692 | ep->txOneCollision++; |
---|
693 | if ((status & kEMACMultColl) != 0) |
---|
694 | ep->txMultiCollision++; |
---|
695 | if ((status & kEMACCollFail) != 0) |
---|
696 | ep->txTooManyCollision++; |
---|
697 | |
---|
698 | if ((status & kEMACSQEFail) != 0) |
---|
699 | ep->txPoorSignal++; |
---|
700 | if ((status & kEMACUnderrun) != 0) |
---|
701 | ep->txUnderrun++; |
---|
702 | } |
---|
703 | while (ep->xmtBusyIndex != scan) { |
---|
704 | m_free(ep->txMBufs[ep->xmtBusyIndex]); |
---|
705 | if (++ep->xmtBusyIndex >= kNXmtDescriptors) ep->xmtBusyIndex = 0; |
---|
706 | } |
---|
707 | if (ep->xmtBusyIndex == ep->xmtFreeIndex) { |
---|
708 | /* Nothing is busy */ |
---|
709 | ep->xmtBusyIndex = kNoXmtBusy; |
---|
710 | break; |
---|
711 | } |
---|
712 | } |
---|
713 | } |
---|
714 | } |
---|
715 | } |
---|
716 | |
---|
717 | |
---|
718 | static void |
---|
719 | SendPacket (EMACLocals* ep, struct ifnet *ifp, struct mbuf *m) |
---|
720 | /* Given a chain of mbufs, set up a transmit description and fire it off */ |
---|
721 | { |
---|
722 | int nAdded, index, lastidx = -1, totalbytes; |
---|
723 | uint16_t status; |
---|
724 | struct mbuf* lastAdded; |
---|
725 | |
---|
726 | nAdded = 0; |
---|
727 | totalbytes = 0; |
---|
728 | lastAdded = NULL; |
---|
729 | index = ep->xmtFreeIndex; |
---|
730 | |
---|
731 | /* Go through the chain of mbufs setting up descriptors for each */ |
---|
732 | while (m != NULL) { |
---|
733 | |
---|
734 | if (m->m_len == 0) { |
---|
735 | /* Can be empty: dispose and unlink from chain */ |
---|
736 | m = m_free(m); |
---|
737 | if (lastAdded!=NULL) lastAdded->m_next = m; |
---|
738 | } else { |
---|
739 | /* Make sure the mbuf has been written to memory */ |
---|
740 | rtems_cache_flush_multiple_data_lines(mtod (m, void *), m->m_len); |
---|
741 | /* If there are no descriptors available wait until there are */ |
---|
742 | while (index == ep->xmtBusyIndex) { |
---|
743 | rtems_event_set events; |
---|
744 | ifp->if_timer = 2; |
---|
745 | /* Then check for free descriptors, followed by: */ |
---|
746 | rtems_bsdnet_event_receive (INTERRUPT_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events); |
---|
747 | FreeTxDescriptors(ep); |
---|
748 | } |
---|
749 | |
---|
750 | /* Fill in a descriptor for this mbuf and record it */ |
---|
751 | ep->txMBufs[index] = m; |
---|
752 | ep->xmtDescTable[index].ptr = mtod (m, void *); |
---|
753 | ep->xmtDescTable[index].adrDataSize = m->m_len; |
---|
754 | /* Fill in ctrlBits as we go but don't mark the first one as ready yet */ |
---|
755 | status = kEMACGenFCS + kEMACGenPad + kEMACRepSrcAddr; |
---|
756 | if (nAdded > 0) |
---|
757 | status |= kMALTxReady; |
---|
758 | if (index==kNXmtDescriptors-1) |
---|
759 | status |= kMALWrap; |
---|
760 | ep->xmtDescTable[index].ctrlBits = status; |
---|
761 | lastidx = index; |
---|
762 | |
---|
763 | totalbytes += m->m_len; |
---|
764 | lastAdded = m; |
---|
765 | m = m->m_next; |
---|
766 | nAdded++; |
---|
767 | |
---|
768 | index += 1; |
---|
769 | if (index==kNXmtDescriptors) |
---|
770 | index = 0; |
---|
771 | |
---|
772 | if (nAdded==kNXmtDescriptors) |
---|
773 | rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); /* This is impossible, of course... */ |
---|
774 | } |
---|
775 | } |
---|
776 | |
---|
777 | _Assert( lastidx != -1 ); |
---|
778 | |
---|
779 | if (nAdded > 0) { |
---|
780 | /* Done and we added some buffers */ |
---|
781 | /* Label the last buffer and ask for an interrupt */ |
---|
782 | ep->xmtDescTable[lastidx].ctrlBits |= kMALLast + kMALInterrupt; |
---|
783 | /* Finally set the ready bit on the first buffer */ |
---|
784 | ep->xmtDescTable[ep->xmtFreeIndex].ctrlBits |= kMALTxReady; |
---|
785 | /* Make sure this has been written */ |
---|
786 | _sync; |
---|
787 | if (ep->xmtBusyIndex == kNoXmtBusy) |
---|
788 | ep->xmtBusyIndex = ep->xmtFreeIndex; |
---|
789 | ep->xmtFreeIndex = index; |
---|
790 | /* Poke the EMAC to get it started (which may not be needed if its already running */ |
---|
791 | ppc405_emac_startxmt(ep); |
---|
792 | ifp->if_timer = 2; |
---|
793 | } |
---|
794 | } |
---|
795 | |
---|
796 | static void |
---|
797 | ppc405_emac_txDaemon (void* param) |
---|
798 | { |
---|
799 | EMACLocals* ep = param; |
---|
800 | struct ifnet *ifp = &ep->arpcom.ac_if; |
---|
801 | struct mbuf *m; |
---|
802 | rtems_event_set events; |
---|
803 | |
---|
804 | ep->xmtFreeIndex = 0; |
---|
805 | ep->xmtBusyIndex = kNoXmtBusy; |
---|
806 | while (TRUE) { |
---|
807 | /* Wait for someone wanting to transmit */ |
---|
808 | rtems_bsdnet_event_receive (START_TRANSMIT_EVENT | INTERRUPT_EVENT, |
---|
809 | RTEMS_EVENT_ANY | RTEMS_WAIT, |
---|
810 | RTEMS_NO_TIMEOUT, |
---|
811 | &events); |
---|
812 | if (events & INTERRUPT_EVENT) |
---|
813 | ifp->if_timer = 0; |
---|
814 | /* Grab packets and send until empty */ |
---|
815 | /* Note that this doesn't (at the time of writing, RTEMS 4.9.1), ever get asked to send more than |
---|
816 | one header mbuf and one data mbuf cluster, regardless of the MTU. This is because sosend() in the FreeBSD |
---|
817 | stack only passes one mbuf at a time across to tcp_send, which immediately sends it */ |
---|
818 | while (TRUE) { |
---|
819 | FreeTxDescriptors(ep); |
---|
820 | IF_DEQUEUE(&ifp->if_snd, m); |
---|
821 | if (m == NULL) |
---|
822 | break; |
---|
823 | SendPacket (ep, ifp, m); |
---|
824 | } |
---|
825 | ifp->if_flags &= ~IFF_OACTIVE; |
---|
826 | } |
---|
827 | } |
---|
828 | |
---|
829 | /*----------------------- The receive daemon/task -------------------------- */ |
---|
830 | |
---|
831 | static void |
---|
832 | MakeRxBuffer(EMACLocals* ep, int index) |
---|
833 | { |
---|
834 | struct mbuf* m; |
---|
835 | |
---|
836 | /* Allocate an mbuf, wait if necessary, label as dynamic data, start of record */ |
---|
837 | MGETHDR (m, M_WAIT, MT_DATA); |
---|
838 | /* Allocate a cluster buffer to this mbuf, waiting if necessary */ |
---|
839 | MCLGET (m, M_WAIT); |
---|
840 | /* Set up reference to the interface the packet will be received on */ |
---|
841 | m->m_pkthdr.rcvif = &ep->arpcom.ac_if; |
---|
842 | ep->rxMBufs[index] = m; |
---|
843 | ep->rcvDescTable[index].ptr = mtod (m, uint8_t*); |
---|
844 | ep->rcvDescTable[index].adrDataSize = 0x0EEE; /* Precaution */ |
---|
845 | if (index==ep->nRxBuffers-1) |
---|
846 | ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt + kMALWrap; |
---|
847 | else |
---|
848 | ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt; |
---|
849 | } |
---|
850 | |
---|
851 | |
---|
852 | |
---|
853 | static void |
---|
854 | ppc405_emac_rxDaemon (void* param) |
---|
855 | { |
---|
856 | EMACLocals* ep = param; |
---|
857 | int index,n,mdex; |
---|
858 | struct mbuf* m; |
---|
859 | struct mbuf* mstart = NULL; |
---|
860 | struct mbuf* mlast = NULL; |
---|
861 | struct ifnet* ifp; |
---|
862 | struct ether_header* eh = NULL; |
---|
863 | rtems_event_set events; |
---|
864 | |
---|
865 | /* Startup : allocate a bunch of receive buffers and point the descriptor table entries at them */ |
---|
866 | ifp = &ep->arpcom.ac_if; |
---|
867 | index = 0; |
---|
868 | while (index < ep->nRxBuffers) { |
---|
869 | MakeRxBuffer(ep,index); |
---|
870 | index += 1; |
---|
871 | } |
---|
872 | index = 0; |
---|
873 | mdex = 0; |
---|
874 | |
---|
875 | /* Loop waiting for frames to arrive */ |
---|
876 | while (TRUE) { |
---|
877 | rtems_bsdnet_event_receive (INTERRUPT_EVENT, |
---|
878 | RTEMS_WAIT | RTEMS_EVENT_ANY, |
---|
879 | RTEMS_NO_TIMEOUT, |
---|
880 | &events); |
---|
881 | while ((ep->rcvDescTable[index].ctrlBits & kMALRxEmpty) == 0) { |
---|
882 | /* Got a frame */ |
---|
883 | uint16_t flags = ep->rcvDescTable[index].ctrlBits; |
---|
884 | if ((flags & kEMACErrMask) != 0) { |
---|
885 | /* It has errors. Update statistics */ |
---|
886 | if ((flags & kEMACOverrun) != 0) |
---|
887 | ep->rxOverrun++; |
---|
888 | if ((flags & kEMACRuntPkt) != 0) |
---|
889 | ep->rxRunt++; |
---|
890 | if ((flags & kEMACBadFCS) != 0) |
---|
891 | ep->rxBadCRC++; |
---|
892 | if ((flags & kEMACAlignErr) != 0) |
---|
893 | ep->rxNonOctet++; |
---|
894 | if ((flags & kEMACPktLong) != 0) |
---|
895 | ep->rxGiant++; |
---|
896 | /* and reset descriptor to empty */ |
---|
897 | |
---|
898 | /* No need to get new mbufs, just reset */ |
---|
899 | ep->rcvDescTable[index].adrDataSize = 0x0EEE; |
---|
900 | if (index==ep->nRxBuffers-1) |
---|
901 | ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt + kMALWrap; |
---|
902 | else |
---|
903 | ep->rcvDescTable[index].ctrlBits = kMALRxEmpty + kMALInterrupt; |
---|
904 | |
---|
905 | } else { |
---|
906 | /* Seems to be OK. Invalidate cache over the size we received */ |
---|
907 | n = ep->rcvDescTable[index].adrDataSize & 0x0FFF; |
---|
908 | m = ep->rxMBufs[index]; |
---|
909 | rtems_cache_invalidate_multiple_data_lines(m->m_data, (n + kCacheLineMask) & ~kCacheLineMask); |
---|
910 | |
---|
911 | /* Consider copying small packets out of the cluster into m_pktdat to save clusters? */ |
---|
912 | m->m_len = n; |
---|
913 | |
---|
914 | /* Jumbo packets will span multiple mbufs; chain them together and submit when we get the last one */ |
---|
915 | if (flags & kMALRxFirst) { |
---|
916 | /* First mbuf in the packet */ |
---|
917 | if (mstart!=NULL) |
---|
918 | rtems_panic("first, no last"); |
---|
919 | |
---|
920 | /* Adjust the mbuf pointers to skip the header and set eh to point to it */ |
---|
921 | m->m_len -= sizeof(struct ether_header); |
---|
922 | m->m_pkthdr.len = m->m_len; |
---|
923 | eh = mtod (m, struct ether_header *); |
---|
924 | m->m_data += sizeof(struct ether_header); |
---|
925 | mstart = m; |
---|
926 | mlast = m; |
---|
927 | mdex = index; |
---|
928 | } else { |
---|
929 | /* Chain onto mstart: add length to pkthdr.len */ |
---|
930 | if (mstart == NULL) |
---|
931 | rtems_panic("last, no first"); |
---|
932 | |
---|
933 | mstart->m_pkthdr.len += n; |
---|
934 | m->m_flags &= ~M_PKTHDR; |
---|
935 | mlast->m_next = m; |
---|
936 | mlast = m; |
---|
937 | } |
---|
938 | |
---|
939 | if (flags & kMALLast) { |
---|
940 | /* Last mbuf in the packet: pass base of the chain to a higher level */ |
---|
941 | ether_input (ifp, eh, mstart); |
---|
942 | |
---|
943 | /* ether_input took the chain, set up new mbufs in the slots we used */ |
---|
944 | mdex -= 1; |
---|
945 | do { |
---|
946 | if (++mdex==ep->nRxBuffers) mdex = 0; |
---|
947 | MakeRxBuffer(ep,mdex); |
---|
948 | } while (mdex != index); |
---|
949 | mstart = NULL; |
---|
950 | mlast = NULL; |
---|
951 | eh = NULL; |
---|
952 | } |
---|
953 | } |
---|
954 | index += 1; |
---|
955 | if (index == ep->nRxBuffers) index = 0; |
---|
956 | } |
---|
957 | } |
---|
958 | } |
---|
959 | |
---|
960 | /*----------- Vectored routines called through the driver struct ------------------ */ |
---|
961 | |
---|
962 | static void ppc405_emac_init (void* p) |
---|
963 | /* Initialise the hardware, create and start the transmit and receive tasks */ |
---|
964 | { |
---|
965 | char txName[] = "ETx0"; |
---|
966 | char rxName[] = "ERx0"; |
---|
967 | |
---|
968 | EMACLocals* ep = (EMACLocals*)p; |
---|
969 | if (ep->txDaemonTid == 0) { |
---|
970 | ppc405_emac_initialize_hardware(ep); |
---|
971 | rxName[3] += ep->phyAddr; |
---|
972 | ep->rxDaemonTid = rtems_bsdnet_newproc (rxName, 4096, ppc405_emac_rxDaemon, ep); |
---|
973 | txName[3] += ep->phyAddr; |
---|
974 | ep->txDaemonTid = rtems_bsdnet_newproc (txName, 4096, ppc405_emac_txDaemon, ep); |
---|
975 | } |
---|
976 | /* Only set IFF_RUNNING if the PHY is ready. If not set the watchdog timer running so we check it */ |
---|
977 | if ( GetPHYLinkState(ep) & kELinkUp ) |
---|
978 | ep->arpcom.ac_if.if_flags |= IFF_RUNNING; |
---|
979 | else |
---|
980 | ep->arpcom.ac_if.if_timer = 1; |
---|
981 | } |
---|
982 | |
---|
983 | static void ppc405_emac_start(struct ifnet *ifp) |
---|
984 | /* Send a packet: send an event to the transmit task, waking it up */ |
---|
985 | { |
---|
986 | EMACLocals* ep = ifp->if_softc; |
---|
987 | rtems_event_send (ep->txDaemonTid, START_TRANSMIT_EVENT); |
---|
988 | ifp->if_flags |= IFF_OACTIVE; |
---|
989 | } |
---|
990 | |
---|
991 | static void ppc405_emac_stop (EMACLocals* ep) |
---|
992 | { |
---|
993 | uint32_t mask; |
---|
994 | |
---|
995 | mask = 0x80000000 >> ep->arpcom.ac_if.if_unit; |
---|
996 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_TXCARR,mask); |
---|
997 | PPC_SET_DEVICE_CONTROL_REGISTER(MAL0_RXCARR,mask); |
---|
998 | ppc405_emac_disable(ep); |
---|
999 | /* *** delete daemons, or do they exit themselves? */ |
---|
1000 | ep->arpcom.ac_if.if_flags &= ~IFF_RUNNING; |
---|
1001 | } |
---|
1002 | |
---|
1003 | #ifdef qDebug |
---|
1004 | static void ppc405_emac_stats (EMACLocals* ep) |
---|
1005 | { |
---|
1006 | |
---|
1007 | printf (" Rx Interrupts:%-8lu", ep->rxInterrupts); |
---|
1008 | printf (" Giant:%-8lu", ep->rxGiant); |
---|
1009 | printf (" Runt:%-8lu\n", ep->rxRunt); |
---|
1010 | printf (" Non-octet:%-8lu", ep->rxNonOctet); |
---|
1011 | printf (" Bad CRC:%-8lu", ep->rxBadCRC); |
---|
1012 | printf (" Overrun:%-8lu\n", ep->rxOverrun); |
---|
1013 | |
---|
1014 | printf (" Tx Interrupts:%-8lu", ep->txInterrupts); |
---|
1015 | printf (" Long deferral:%-8lu", ep->txDeferred); |
---|
1016 | printf (" No Carrier:%-8lu\n", ep->txLostCarrier); |
---|
1017 | printf (" Late collision:%-8lu", ep->txLateCollision); |
---|
1018 | printf (" One collision:%-8lu", ep->txOneCollision); |
---|
1019 | printf (" Many collisions:%-8lu\n", ep->txMultiCollision); |
---|
1020 | printf ("Excess collisions:%-8lu", ep->txTooManyCollision); |
---|
1021 | printf (" Underrun:%-8lu", ep->txUnderrun); |
---|
1022 | printf (" Poor signal:%-8lu\n", ep->txPoorSignal); |
---|
1023 | } |
---|
1024 | #endif |
---|
1025 | |
---|
1026 | static int UpdateMulticast(EMACLocals* ep) |
---|
1027 | { |
---|
1028 | /* Traverse list of multicast addresses and update hardware hash filter. This is just a work-reduction */ |
---|
1029 | /* step; the filter uses a hash of the hardware address and therefore doesn't catch all unwanted packets */ |
---|
1030 | /* We have to do other checks in software. */ |
---|
1031 | /* 405GP/GPr has 4x16-bit hash registers, 405EX/EXr has 8x32-bit */ |
---|
1032 | |
---|
1033 | struct ether_multi* enm; |
---|
1034 | struct ether_multistep step; |
---|
1035 | uint32_t hash; |
---|
1036 | |
---|
1037 | #ifdef qMultiDebug |
---|
1038 | printf("\nMulticast etheraddrs:\n"); |
---|
1039 | #endif |
---|
1040 | |
---|
1041 | ETHER_FIRST_MULTI(step, &ep->arpcom, enm); |
---|
1042 | while (enm != NULL) { |
---|
1043 | |
---|
1044 | /* *** Doesn't implement ranges */ |
---|
1045 | |
---|
1046 | hash = ether_crc32_be( (uint8_t*)&enm->enm_addrlo, sizeof(enm->enm_addrlo) ); |
---|
1047 | if (get_ppc_cpu_type() == PPC_405EX) { |
---|
1048 | hash >>= 24; /* Upper 8 bits, split 3/5 */ |
---|
1049 | /* This has been experimentally verified against the hardware */ |
---|
1050 | ep->EMAC->e_groupHash[7-(hash>>5)] |= (1 << (hash & 0x1F)); |
---|
1051 | } else { |
---|
1052 | hash >>= 26; /* Upper 6 bits, split 2/4 */ |
---|
1053 | /* This has not been checked */ |
---|
1054 | ep->EMAC->g_groupHash[3-(hash>>6)] |= (1 << (hash & 0xF)); |
---|
1055 | } |
---|
1056 | |
---|
1057 | #ifdef qMultiDebug |
---|
1058 | printf(" "); |
---|
1059 | printaddr(enm->enm_addrlo); |
---|
1060 | printf(" = bit %d",hash); |
---|
1061 | if (memcmp(&enm->enm_addrlo, &enm->enm_addrhi, 6) != 0) { |
---|
1062 | printf(" - "); |
---|
1063 | printaddr(enm->enm_addrhi); |
---|
1064 | printf(" [not supported]"); |
---|
1065 | } |
---|
1066 | printf("\n"); |
---|
1067 | #endif |
---|
1068 | |
---|
1069 | ETHER_NEXT_MULTI(step, enm); |
---|
1070 | } |
---|
1071 | #ifdef qMultiDebug |
---|
1072 | { |
---|
1073 | int i; |
---|
1074 | printf(" Grouphash is "); |
---|
1075 | for (i=0; i<8; i++) |
---|
1076 | printf("%08X:",(int)ep->EMAC->e_groupHash[i]); |
---|
1077 | printf("\n"); |
---|
1078 | } |
---|
1079 | #endif |
---|
1080 | return 0; |
---|
1081 | } |
---|
1082 | |
---|
1083 | |
---|
1084 | static int ppc405_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) |
---|
1085 | { |
---|
1086 | int error = 0; |
---|
1087 | EMACLocals* ep = ifp->if_softc; |
---|
1088 | struct ifreq* reqP = (struct ifreq *) data; |
---|
1089 | |
---|
1090 | switch (command) { |
---|
1091 | |
---|
1092 | case SIOCSIFFLAGS: |
---|
1093 | switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { |
---|
1094 | case IFF_RUNNING: |
---|
1095 | ppc405_emac_stop(ep); |
---|
1096 | break; |
---|
1097 | |
---|
1098 | case IFF_UP: |
---|
1099 | ppc405_emac_init(ep); |
---|
1100 | break; |
---|
1101 | |
---|
1102 | case IFF_UP | IFF_RUNNING: |
---|
1103 | ppc405_emac_stop(ep); |
---|
1104 | ppc405_emac_init(ep); |
---|
1105 | break; |
---|
1106 | |
---|
1107 | default: |
---|
1108 | break; |
---|
1109 | } |
---|
1110 | break; |
---|
1111 | |
---|
1112 | case SIOCADDMULTI: { |
---|
1113 | error = ether_addmulti( reqP, &ep->arpcom); |
---|
1114 | if (error==ENETRESET) |
---|
1115 | error = UpdateMulticast(ep); |
---|
1116 | } break; |
---|
1117 | |
---|
1118 | case SIOCDELMULTI: |
---|
1119 | error = ether_delmulti( (struct ifreq *) data, &ep->arpcom); |
---|
1120 | if (error==ENETRESET) |
---|
1121 | error = UpdateMulticast(ep); |
---|
1122 | break; |
---|
1123 | |
---|
1124 | case SIOCSIFMTU: { |
---|
1125 | /* Note that this may not do what you want; setting the interface MTU doesn't touch the route MTUs, |
---|
1126 | and new routes are sometimes made by cloning old ones. So this won't change the MTU to known hosts |
---|
1127 | and may not change the MTU to new ones either... */ |
---|
1128 | int max; |
---|
1129 | if ( get_ppc_cpu_type() == PPC_405EX && (ep->EMAC->mode1 & keEMACJumbo) != 0 ) |
---|
1130 | max = ETHER_MAX_LEN_JUMBO; |
---|
1131 | else |
---|
1132 | max = ETHER_MAX_LEN; |
---|
1133 | if (reqP->ifr_mtu > max - ETHER_HDR_LEN - ETHER_CRC_LEN) |
---|
1134 | error = EINVAL; |
---|
1135 | else |
---|
1136 | ifp->if_mtu = reqP->ifr_mtu; |
---|
1137 | } break; |
---|
1138 | |
---|
1139 | case SIO_RTEMS_SHOW_STATS: |
---|
1140 | #ifdef qDebug |
---|
1141 | ppc405_emac_stats(ep); |
---|
1142 | #endif |
---|
1143 | break; |
---|
1144 | |
---|
1145 | default: |
---|
1146 | /* Not handled here, pass to generic */ |
---|
1147 | error = ether_ioctl(ifp,command,data); |
---|
1148 | break; |
---|
1149 | } |
---|
1150 | |
---|
1151 | #ifdef qDebug |
---|
1152 | if (error != 0) |
---|
1153 | printf("--- Ethernet ioctl %d failed %d\n",(int)command,error); |
---|
1154 | #endif |
---|
1155 | |
---|
1156 | return error; |
---|
1157 | } |
---|
1158 | |
---|
1159 | |
---|
1160 | /*----------------------- External attach function -------------------------- |
---|
1161 | * |
---|
1162 | * This is the one function we are required to define in here: declared in bsp.h |
---|
1163 | * as RTEMS_BSP_NETWORK_DRIVER_ATTACH and called from rtems_bsdnet_attach |
---|
1164 | * |
---|
1165 | */ |
---|
1166 | |
---|
1167 | int |
---|
1168 | rtems_emac_driver_attach(struct rtems_bsdnet_ifconfig* config, int attaching) |
---|
1169 | { |
---|
1170 | int unitNumber, nUnits; |
---|
1171 | char* unitName; |
---|
1172 | struct ifnet* ifp; |
---|
1173 | EMACLocals* ep; |
---|
1174 | |
---|
1175 | if (attaching==0) { |
---|
1176 | printk ("EMAC: driver cannot be detached.\n"); |
---|
1177 | return 0; |
---|
1178 | } |
---|
1179 | |
---|
1180 | nUnits = 1; |
---|
1181 | if (get_ppc_cpu_type()==PPC_405EX && get_ppc_cpu_revision() > 0x1474) |
---|
1182 | nUnits = 2; /* PPC405EX has two interfaces, EXr has one */ |
---|
1183 | |
---|
1184 | unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName); |
---|
1185 | if (unitNumber < 0 || unitNumber > nUnits-1) { |
---|
1186 | printk ("EMAC: bad unit number %d.\n",unitNumber); |
---|
1187 | return 0; |
---|
1188 | } |
---|
1189 | |
---|
1190 | ep = &gEmacs[unitNumber]; |
---|
1191 | |
---|
1192 | if (get_ppc_cpu_type()==PPC_405EX) { |
---|
1193 | if (unitNumber==0) ep->EMAC = (EthernetRegisters_EX*)EMAC0EXAddress; |
---|
1194 | else ep->EMAC = (EthernetRegisters_GP*)EMAC1EXAddress; |
---|
1195 | } else |
---|
1196 | ep->EMAC = (EthernetRegisters_GP*)EMAC0GPAddress; |
---|
1197 | |
---|
1198 | ifp = &ep->arpcom.ac_if; |
---|
1199 | if (ifp->if_softc != NULL) { |
---|
1200 | printk ("EMAC: driver already in use.\n"); |
---|
1201 | return 0; |
---|
1202 | } |
---|
1203 | ifp->if_softc = ep; |
---|
1204 | |
---|
1205 | if (config->hardware_address == 0) |
---|
1206 | rtems_panic("No Ethernet MAC address specified!"); |
---|
1207 | memcpy (ep->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); |
---|
1208 | |
---|
1209 | ifp->if_name = unitName; |
---|
1210 | ifp->if_unit = unitNumber; |
---|
1211 | |
---|
1212 | if (config->mtu != 0) |
---|
1213 | ifp->if_mtu = config->mtu; |
---|
1214 | else |
---|
1215 | ifp->if_mtu = ETHERMTU; /* May be adjusted later by ppc405_emac_phy_adapt() */ |
---|
1216 | |
---|
1217 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
---|
1218 | if (ifp->if_snd.ifq_maxlen == 0) |
---|
1219 | ifp->if_snd.ifq_maxlen = ifqmaxlen; |
---|
1220 | ifp->if_init = &ppc405_emac_init; |
---|
1221 | ifp->if_ioctl = ppc405_emac_ioctl; |
---|
1222 | ifp->if_start = ppc405_emac_start; |
---|
1223 | ifp->if_output = ether_output; |
---|
1224 | ifp->if_watchdog = ppc405_emac_watchdog; |
---|
1225 | ifp->if_timer = 0; |
---|
1226 | |
---|
1227 | if (config->rbuf_count != 0) { |
---|
1228 | if (config->rbuf_count > 256) ep->nRxBuffers = 256; |
---|
1229 | else ep->nRxBuffers = config->rbuf_count; |
---|
1230 | } else |
---|
1231 | ep->nRxBuffers = nmbclusters/2; |
---|
1232 | |
---|
1233 | ep->phyAddr = unitNumber+1; |
---|
1234 | ep->phyState = 0; |
---|
1235 | |
---|
1236 | #ifdef qDebug |
---|
1237 | printf("\n Setting up EMAC %d of %d\n",unitNumber+1,nUnits); |
---|
1238 | printf(" MAC address is "); |
---|
1239 | printaddr(ep->arpcom.ac_enaddr); |
---|
1240 | printf(" MHLEN = %d, MINCLSIZE = %d MCLBYTES = %d\n",MHLEN,MINCLSIZE,MCLBYTES); |
---|
1241 | printf(" ticks/sec = %d, usec/tick = %d\n", rtems_bsdnet_ticks_per_second, rtems_bsdnet_microseconds_per_tick); |
---|
1242 | #endif |
---|
1243 | |
---|
1244 | if_attach (ifp); |
---|
1245 | ether_ifattach (ifp); |
---|
1246 | |
---|
1247 | return 1; |
---|
1248 | } |
---|
1249 | |
---|