]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/devs/eth/cl/cs8900a/v2_0/src/if_cs8900a.c
c04899f749108bcffa0dcbd183ae0f97d982dd13
[karo-tx-redboot.git] / packages / devs / eth / cl / cs8900a / v2_0 / src / if_cs8900a.c
1 //==========================================================================
2 //
3 //      dev/if_cs8900a.c
4 //
5 //      Device driver for Cirrus Logic CS8900A ethernet controller
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //####BSDCOPYRIGHTBEGIN####
41 //
42 // -------------------------------------------
43 //
44 // Portions of this software may have been derived from OpenBSD or other sources,
45 // and are covered by the appropriate copyright disclaimers included herein.
46 //
47 // -------------------------------------------
48 //
49 //####BSDCOPYRIGHTEND####
50 //==========================================================================
51 //#####DESCRIPTIONBEGIN####
52 //
53 // Author(s):    gthomas
54 // Contributors: gthomas, jskov
55 // Date:         2001-11-02
56 // Purpose:      
57 // Description:  Driver for CS8900 ethernet controller
58 //
59 // Note:         Platform can define CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
60 //               to get a timer thread polling instead of interupt based
61 //               operation.
62 //
63 // Note:         Driver will need some changes to support multiple instances
64 //
65 //####DESCRIPTIONEND####
66 //
67 //==========================================================================
68
69 #include <pkgconf/system.h>
70 #ifdef CYGPKG_KERNEL
71 #include <cyg/kernel/kapi.h>
72 #endif
73 #include <pkgconf/io_eth_drivers.h>
74
75 #include <cyg/infra/cyg_type.h>
76 #include <cyg/infra/cyg_ass.h>
77 #include <cyg/hal/hal_arch.h>
78 #include <cyg/hal/hal_intr.h>
79 #include <cyg/hal/hal_endian.h>
80 #include <cyg/infra/diag.h>
81 #include <cyg/hal/drv_api.h>
82 #undef __ECOS
83 #define __ECOS
84 #include <cyg/io/eth/eth_drv.h>
85 #include <cyg/io/eth/netdev.h>
86
87 #include <cyg/io/cs8900.h>
88
89 #define __WANT_DEVS
90 #include CYGDAT_DEVS_ETH_CL_CS8900A_INL
91 #undef __WANT_DEVS
92
93 // NOINTS operation only relevant when the NET package is loaded
94 #if !defined(CYGPKG_NET) || !defined(CYGPKG_KERNEL)
95 # undef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
96 #endif
97
98 #ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
99 #define STACK_SIZE CYGNUM_HAL_STACK_SIZE_MINIMUM
100 static char cs8900a_fake_int_stack[STACK_SIZE];
101 static cyg_thread cs8900a_fake_int_thread_data;
102 static cyg_handle_t cs8900a_fake_int_thread_handle;
103 static void cs8900a_fake_int(cyg_addrword_t);
104 #endif
105
106 #ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
107 extern int cyg_io_eth_net_debug;
108 #endif
109
110 static void cs8900a_poll(struct eth_drv_sc *sc);
111 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
112 // This ISR is called when the ethernet interrupt occurs
113 static int
114 cs8900a_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs)
115 {
116     cs8900a_priv_data_t* cpd = (cs8900a_priv_data_t *)data;
117     cyg_drv_interrupt_mask(cpd->interrupt);
118     cyg_drv_interrupt_acknowledge(cpd->interrupt);
119     return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
120 }
121
122 static void
123 cs8900a_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
124 {
125     // This conditioning out is necessary because of explicit calls to this
126     // DSR - which would not ever be called in the case of a polled mode
127     // usage ie. in RedBoot.
128 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
129     cs8900a_priv_data_t* cpd = (cs8900a_priv_data_t *)data;
130     struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->tab);
131     struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);
132
133     DEBUG_FUNCTION();
134
135     // but here, it must be a *sc:
136     eth_drv_dsr( vector, count, (cyg_addrword_t)sc );
137 #else
138 # ifndef CYGPKG_REDBOOT
139 #  error Empty CS8900A ethernet DSR is compiled.  Is this what you want?
140 # endif
141 #endif
142 }
143 #endif
144
145 // The deliver function (ex-DSR)  handles the ethernet [logical] processing
146 static void
147 cs8900a_deliver(struct eth_drv_sc *sc)
148 {
149     cs8900a_poll(sc);
150 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
151     {
152         cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
153         // Allow interrupts to happen again
154         cyg_drv_interrupt_unmask(cpd->interrupt);
155     }
156 #endif
157 }
158
159 static int
160 cs8900a_int_vector(struct eth_drv_sc *sc)
161 {
162     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
163     return (cpd->interrupt);
164 }
165
166 static bool 
167 cs8900a_init(struct cyg_netdevtab_entry *tab)
168 {
169     struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
170     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
171     cyg_addrword_t base = cpd->base;
172     cyg_uint16 chip_type, chip_rev, chip_status;
173     cyg_uint16 i;
174 #ifndef CS8900A_RESET_BYPASS
175     long timeout = 500000;
176 #endif
177     cyg_bool esa_configured = false;
178     
179     cpd->tab = tab;
180
181     CYGHWR_CL_CS8900A_PLF_INIT(cpd);
182
183 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
184     // Initialize environment, setup interrupt handler
185     cyg_drv_interrupt_create(cpd->interrupt,
186                              0, // Priority - what goes here?
187                              (cyg_addrword_t)cpd, //  Data item passed to interrupt handler
188                              (cyg_ISR_t *)cs8900a_isr,
189                              (cyg_DSR_t *)cs8900a_dsr,
190                              &cpd->interrupt_handle,
191                              &cpd->interrupt_object);
192     cyg_drv_interrupt_attach(cpd->interrupt_handle);
193     cyg_drv_interrupt_acknowledge(cpd->interrupt);
194     cyg_drv_interrupt_unmask(cpd->interrupt);
195
196 #ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
197     cyg_thread_create(1,                 // Priority
198                       cs8900a_fake_int,   // entry
199                       (cyg_addrword_t)sc, // entry parameter
200                       "CS8900 int",      // Name
201                       &cs8900a_fake_int_stack[0],         // Stack
202                       STACK_SIZE,        // Size
203                       &cs8900a_fake_int_thread_handle,    // Handle
204                       &cs8900a_fake_int_thread_data       // Thread data structure
205             );
206     cyg_thread_resume(cs8900a_fake_int_thread_handle);  // Start it
207 #endif
208 #endif
209
210     // Read controller ID - the first is a dummy read, since (on some
211     // platforms) the first access to the controller seems to skip the
212     // MSB 8 bits.
213     get_reg(base, PP_ChipID);
214     chip_type = get_reg(base, PP_ChipID);
215     chip_rev = get_reg(base, PP_ChipRev);
216 #if DEBUG & 8
217     diag_printf("CS8900A[%p] - type: 0x%04x, rev: 0x%04x\n", base, chip_type, chip_rev);
218 #endif
219     if (chip_type != PP_ChipID_CL) {
220 #if DEBUG & 8
221         diag_printf("CS8900 - invalid type (0x%04x), must be 0x630e\n", chip_type);
222 #endif
223         return false;
224     }
225
226     CYGHWR_CL_CS8900A_PLF_RESET(base);
227 #ifndef CS8900A_RESET_BYPASS
228     put_reg(base, PP_SelfCtl, PP_SelfCtl_Reset);  // Reset chip
229
230     CYGHWR_CL_CS8900A_PLF_POST_RESET(base);
231     
232     while ((get_reg(base, PP_SelfStat) & PP_SelfStat_InitD) == 0) {
233         if (--timeout <= 0) {
234 #if DEBUG & 8
235             diag_printf("CS8900 didn't reset - abort!\n");
236 #endif
237             return false;
238         }
239     }
240 #endif /* CS8900A_RESET_BYPASS */
241
242     chip_status = get_reg(base, PP_SelfStat);
243 #if DEBUG & 8
244     diag_printf("CS8900 - status: 0x%04x (%sEEPROM present)\n", chip_status,
245                 chip_status & PP_SelfStat_EEPROM ? "" : "no ");
246 #endif
247
248
249     // Disable reception whilst finding the ESA
250     put_reg(base, PP_LineCTL, 0 );
251     // Find ESA - check possible sources in sequence and stop when
252     // one provides the ESA:
253     //   RedBoot option (via provide_esa)
254     //   Compile-time configuration
255     //   EEPROM
256     //   <fail configuration of device>
257     if (NULL != cpd->provide_esa) {
258         esa_configured = cpd->provide_esa(cpd);
259 # if DEBUG & 8
260         if (esa_configured)
261             diag_printf("Got ESA from RedBoot option\n");
262 # endif
263     }
264     if (!esa_configured && cpd->hardwired_esa) {
265         // ESA is already set in cpd->esa[]
266 #if DEBUG & 8
267         diag_printf("Got hardcoded ESA\n");
268 #endif
269         esa_configured = true;
270     }
271     if (!esa_configured && (chip_status & PP_SelfStat_EEPROM)) {
272         // Get ESA from EEPROM - via the PP_IA registers
273         cyg_uint16 esa_word;
274         for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
275 #ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
276             esa_word = get_reg(base, PP_IA+i);
277             cpd->esa[i] = (esa_word & 0xFF);
278             cpd->esa[i+1] = (esa_word >> 8) & 0xFF;
279 #else
280             esa_word = get_reg(base, PP_IA+CYG_SWAP16(i));
281             cpd->esa[i+1] = (esa_word & 0xFF);
282             cpd->esa[i] = (esa_word >> 8) & 0xFF;
283 #endif
284         }
285 #if DEBUG & 8
286         diag_printf("Got EEPROM ESA\n");
287 #endif
288         esa_configured = true;
289     }
290     if (!esa_configured) {
291 # if DEBUG & 8
292         diag_printf("CS8900 - no EEPROM, static ESA or RedBoot config option.\n");
293 # endif
294         return false;
295     }
296
297     // Tell the chip what ESA to use
298     for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
299 #ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
300         put_reg(base, PP_IA+i, cpd->esa[i] | (cpd->esa[i+1] << 8));
301 #else
302         put_reg(base, PP_IA+CYG_SWAP16(i), cpd->esa[i+1] | (cpd->esa[i] << 8));
303 #endif
304     }
305     // Set logical address mask
306     for (i = 0;  i < 8;  i += 2) {
307 #ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
308         put_reg(base, PP_LAF+i, 0xFFFF);
309 #else
310         put_reg(base, PP_LAF+CYG_SWAP16(i), 0xFFFF);
311 #endif
312     }
313 # if DEBUG & 8
314     diag_printf("ESA %02x:%02x:%02x:%02x:%02x:%02x\n",
315                 cpd->esa[0], cpd->esa[1], cpd->esa[2],
316                 cpd->esa[3], cpd->esa[4], cpd->esa[5]);
317 # endif
318
319     // Initialize upper level driver
320     (sc->funs->eth_drv->init)(sc, cpd->esa);
321
322     return true;
323 }
324
325 static void
326 cs8900a_stop(struct eth_drv_sc *sc)
327 {
328     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
329     cyg_addrword_t base = cpd->base;
330
331     put_reg(base, PP_LineCTL, 0);
332 }
333
334 // This function is called to "start up" the interface.  It may be called
335 // multiple times, even when the hardware is already running.  It will be
336 // called whenever something "hardware oriented" changes and should leave
337 // the hardware ready to send/receive packets.
338 static void
339 cs8900a_start(struct eth_drv_sc *sc, cyg_uint8 *esa, int flags)
340 {
341     cyg_uint16 stat;
342     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
343     cyg_addrword_t base = cpd->base;
344
345     put_reg(base, PP_BusCtl, PP_BusCtl_MemoryE);  // Disable interrupts, memory mode
346     put_reg(base, PP_IntReg, PP_IntReg_IRQ0);  // Only possibility
347     put_reg(base, PP_RxCFG, PP_RxCFG_RxOK | PP_RxCFG_CRC | 
348                       PP_RxCFG_RUNT | PP_RxCFG_EXTRA);
349     cpd->rxmode = PP_RxCTL_RxOK | PP_RxCTL_Broadcast | PP_RxCTL_IA;
350     put_reg(base, PP_RxCTL, cpd->rxmode);
351     put_reg(base, PP_TxCFG, PP_TxCFG_TxOK | PP_TxCFG_Collision | 
352                       PP_TxCFG_CRS | PP_TxCFG_SQE | PP_TxCFG_Late | 
353                       PP_TxCFG_Jabber | PP_TxCFG_16Collisions);
354     put_reg(base, PP_BufCFG, PP_BufCFG_TxRDY | PP_BufCFG_TxUE | PP_BufCFG_RxMiss | 
355                        PP_BufCFG_TxCol | PP_BufCFG_Miss | PP_BufCFG_SWI);
356     put_reg(base, PP_IntReg, PP_IntReg_IRQ0);  // Only possibility
357     put_reg(base, PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);
358     // Clear Interrupt Status Queue before enabling interrupts
359     do {
360         HAL_READ_UINT16(cpd->base+CS8900A_ISQ, stat);
361     }  while (stat != 0) ;
362     cpd->txbusy = false;
363     put_reg(base, PP_BusCtl, PP_BusCtl_EnableIRQ);
364 }
365
366 // This routine is called to perform special "control" opertions
367 static int
368 cs8900a_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length)
369 {
370     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
371     cyg_addrword_t base = cpd->base;
372     struct eth_drv_mc_list *mc_list = data;
373     unsigned char *esa = (unsigned char *)data;
374     int i;
375
376     switch (key) {
377     case ETH_DRV_SET_MAC_ADDRESS:
378 #if 9 & DEBUG
379         diag_printf("CS8900A - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
380                 esa[0], esa[1], esa[2],
381                 esa[3], esa[4], esa[5] );
382 #if !defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) || !defined(CS8900A_PROGRAM_EEPROM)
383         diag_printf("*** PERMANENT EEPROM WRITE NOT ENABLED ***\n");
384 #endif
385 #endif // DEBUG
386
387         // We can write the MAC address into the interface info,
388         // and the chip registers no problem.
389         for ( i = 0; i < sizeof(cpd->esa);  i++ )
390             cpd->esa[i] = esa[i];
391         for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
392             cyg_uint16 reg = cpd->esa[i] | (cpd->esa[i+1] << 8);
393             put_reg(cpd->base, PP_IA+i, reg );
394         }
395 #if defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) && defined(CS8900A_PROGRAM_EEPROM)
396         if (CS8900A_PROGRAM_EEPROM(cpd))
397             return 1;
398         else 
399             return 0;
400 #elif defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) && !defined(CS8900A_PROGRAM_EEPROM)
401         /* WRITE_EEPROM requested, but no PROGRAM_EEPROM provided */
402         return 1;
403 #else /* !CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM - No need to write EEPROM */
404         return 0;
405 #endif
406
407 #ifdef ETH_DRV_GET_MAC_ADDRESS
408     case ETH_DRV_GET_MAC_ADDRESS:
409         // Extract the MAC address that is in the chip, and tell the
410         // system about it.
411         for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
412             unsigned short z = get_reg(cpd->base, PP_IA+i/2 );
413             esa[i] =   (unsigned char)(0xff & z);
414             esa[i+1] = (unsigned char)(0xff & (z >> 8));
415         }
416         return 0;
417 #endif
418     case ETH_DRV_SET_MC_LIST:
419     case ETH_DRV_SET_MC_ALL:
420         // Note: this code always accepts all multicast addresses if any
421         // are desired.  It would be possible to accept a subset by adjusting
422         // the Logical Address Filter (LAF), but that would require scanning
423         // this list and building a suitable mask.
424         if (mc_list->len) {
425             cpd->rxmode |= PP_RxCTL_Multicast;
426         } else {
427             cpd->rxmode &= ~PP_RxCTL_Multicast;
428         }
429         put_reg(base, PP_RxCTL, cpd->rxmode);  // When is it safe to do this?
430         return 0;
431     default:
432         return 1;
433         break;
434     }
435 }
436
437 // This routine is called to see if it is possible to send another packet.
438 // It will return non-zero if a transmit is possible, zero otherwise.
439 static int
440 cs8900a_can_send(struct eth_drv_sc *sc)
441 {
442     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
443     cyg_addrword_t base = cpd->base;
444     cyg_uint16 stat;
445
446     stat = get_reg(base, PP_LineStat);
447     if ((stat & PP_LineStat_LinkOK) == 0) {
448         return false;  // Link not connected
449     }
450 #ifdef CYGPKG_KERNEL
451     // Horrible hack!
452     if (cpd->txbusy) {
453         cyg_tick_count_t now = cyg_current_time();
454         if ((now - cpd->txstart) > 25) {
455             // 250ms is more than enough to transmit one frame
456 #if DEBUG & 1
457             diag_printf("CS8900: Tx interrupt lost\n");
458 #endif
459             cpd->txbusy = false;
460             // Free up the buffer (with error indication)
461             (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 1);
462         }
463     }
464 #endif
465     return (cpd->txbusy == false);
466 }
467
468 // This routine is called to send data to the hardware.
469 static void 
470 cs8900a_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
471             int total_len, unsigned long key)
472 {
473     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
474     cyg_addrword_t base = cpd->base;
475     int i;
476     int len;
477     cyg_uint8 *data;
478     cyg_uint16 saved_data = 0, *sdata;
479     cyg_uint16 stat;
480     bool odd_byte = false;
481     int timeout = 50000;
482
483     // Mark xmitter busy
484     cpd->txbusy = true;
485     cpd->txkey = key;
486 #ifdef CYGPKG_KERNEL
487     cpd->txstart = cyg_current_time();
488 #endif
489
490     // Start the xmit sequence
491 #ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
492     total_len = CYG_SWAP16(total_len);
493 #endif
494         
495     // The hardware indicates that there are options as to when the actual
496     // packet transmission will start wrt moving of data into the transmit
497     // buffer.  However, impirical results seem to indicate that if the
498     // packet is large and transmission is allowed to start before the
499     // entire packet has been pushed into the buffer, the hardware gets
500     // confused and the packet is lost, along with a "lost" Tx interrupt.
501     // This may be a case of the copy loop below being interrupted, e.g.
502     // a system timer interrupt, and the hardware getting unhappy that 
503     // not all of the data was provided before the transmission should
504     // have completed (i.e. buffer underrun).
505     // For now, the solution is to not allow this overlap.
506     //HAL_WRITE_UINT16(cpd->base+CS8900A_TxCMD, PP_TxCmd_TxStart_5)
507
508     // Start only when all data sent to chip
509     HAL_WRITE_UINT16(cpd->base+CS8900A_TxCMD, PP_TxCmd_TxStart_Full);
510
511     HAL_WRITE_UINT16(cpd->base+CS8900A_TxLEN, total_len);
512     // Wait for controller ready signal
513     do {
514         stat = get_reg(base, PP_BusStat);
515         if (timeout-- < 0) {
516             return;
517         }
518     } while (!(stat & PP_BusStat_TxRDY));
519
520     if (timeout < 0) {
521         diag_printf("if_cs8900a.c:  PP_BusStat_TXRDY is not set. Cannot transmit packet\n");
522         return;
523     }
524     // Put data into buffer
525     for (i = 0;  i < sg_len;  i++) {
526         data = (cyg_uint8 *)sg_list[i].buf;
527         len = sg_list[i].len;
528
529         if (len > 0) {
530             /* Finish the last word. */
531             if (odd_byte) {
532 // This new byte must get on the bus _after_ the last saved odd byte, it therefore
533 // belongs in the MSB of the CS8900a
534 #ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED                            
535                 saved_data |= *data++;
536 #else
537                 saved_data |= ((cyg_uint16)*data++) << 8;
538 #endif
539                 HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
540                 len--;
541                 odd_byte = false;
542             }
543             if (((CYG_ADDRESS)data & 0x1) == 0) {
544                 /* Aligned on 16-bit boundary, so output contiguous words. */
545                 sdata = (cyg_uint16 *)data;
546                 while (len > 1) {
547                                         // Make sure data get on the bus in Big Endian format
548 #if((CYG_BYTEORDER == CYG_MSBFIRST) && defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED) || \
549     (CYG_BYTEORDER == CYG_LSBFIRST) && !defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED ))
550                     HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, *sdata++);
551 #else
552                     HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, CYG_SWAP16(*sdata++));
553 #endif
554                     len -= sizeof(cyg_uint16);
555                 }
556                 data = (cyg_uint8 *)sdata;
557             } else {
558                 /* Not 16-bit aligned, so byte copy */
559                 while (len > 1) {
560                     saved_data = (cyg_uint16)*data++;   // reuse saved_data
561                                         // Make sure data get on the bus in Big Endian format, the first byte belongs in the
562                                         // LSB of the CS8900A
563 #ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
564                     saved_data =  ((cyg_uint16)*data++) | (saved_data << 8);
565 #else
566                     saved_data |= ((cyg_uint16)*data++) << 8;
567 #endif
568                     HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
569                     len -= sizeof(cyg_uint16);
570                 }
571             }
572             /* Save last byte, if necessary. */
573             if (len == 1) {
574                 saved_data = (cyg_uint16)*data;
575 // This _last_ byte must get on the bus _first_, it therefore belongs in the LSB of
576 // the CS8900a
577 #ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
578                 saved_data = (saved_data << 8);
579 #endif
580                 odd_byte = true;
581             }
582         }
583     }
584     if (odd_byte) {
585         HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
586     }
587 }
588
589 // This function is called when a packet has been received.  It's job is
590 // to prepare to unload the packet from the hardware.  Once the length of
591 // the packet is known, the upper layer of the driver can be told.  When
592 // the upper layer is ready to unload the packet, the internal function
593 // 'cs8900a_recv' will be called to actually fetch it from the hardware.
594 static void
595 cs8900a_RxEvent(struct eth_drv_sc *sc, int stat)
596 {
597     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
598     cyg_addrword_t base = cpd->base;
599     cyg_uint16 len;
600
601     if(stat & PP_RxCFG_RxOK) {
602         // Only start reading a message if one has been received
603         HAL_READ_UINT16(base+CS8900A_RTDATA, stat);
604         HAL_READ_UINT16(base+CS8900A_RTDATA, len);
605
606 #ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
607         len = CYG_SWAP16(len);
608 #endif
609
610         CYG_ASSERT(len > 0, "Zero length ethernet frame received");
611         
612 #ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
613         if (cyg_io_eth_net_debug) {
614             diag_printf("RxEvent - stat: %x, len: %d\n", stat, len);
615         }
616 #endif
617         (sc->funs->eth_drv->recv)(sc, len);
618     }
619 }
620
621 // This function is called as a result of the "eth_drv_recv()" call above.
622 // It's job is to actually fetch data for a packet from the hardware once
623 // memory buffers have been allocated for the packet.  Note that the buffers
624 // may come in pieces, using a scatter-gather list.  This allows for more
625 // efficient processing in the upper layers of the stack.
626 static void
627 cs8900a_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
628 {
629     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
630     cyg_addrword_t base = cpd->base;
631     int i, mlen;
632     cyg_uint16 *data, val;
633     cyg_uint8 *cp, cval;
634
635     for (i = 0;  i < sg_len;  i++) {
636         data = (cyg_uint16 *)sg_list[i].buf;
637         mlen = sg_list[i].len;
638         while (mlen >= sizeof(*data)) {
639             HAL_READ_UINT16(base+CS8900A_RTDATA, val);
640             if (data) {
641 #if((CYG_BYTEORDER == CYG_MSBFIRST) && defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED) || \
642     (CYG_BYTEORDER == CYG_LSBFIRST) && !defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED ))
643                 *data++ = val;
644 #else
645                 *data++ = CYG_SWAP16(val);
646 #endif
647             }
648             mlen -= sizeof(*data);
649         }
650         if (mlen) {
651             HAL_READ_UINT16(base+CS8900A_RTDATA, val);
652 #ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED 
653             // last odd byte will be in the LSB
654             cval = (cyg_uint8)(val);
655 #elif(CYG_BYTEORDER == CYG_MSBFIRST)
656             // last odd byte will be in the MSB
657             cval = (cyg_uint8)(val >> 8);
658 #endif
659             cval &= 0xff;
660             if ((cp = (cyg_uint8 *)data) != 0) {
661                 *cp = cval;
662             }
663         }
664     }
665 }
666
667 static void
668 cs8900a_TxEvent(struct eth_drv_sc *sc, int stat)
669 {
670     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
671     cyg_addrword_t base = cpd->base;
672
673     stat = get_reg(base, PP_TER);
674 #ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
675     if (cyg_io_eth_net_debug) {
676         diag_printf("Tx event: %x\n", stat);
677     }
678 #endif
679     cpd->txbusy = false;
680     (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
681 }
682
683 static void
684 cs8900a_BufEvent(struct eth_drv_sc *sc, int stat)
685 {
686     if (stat & PP_BufCFG_RxMiss) {
687     }
688     if (stat & PP_BufCFG_TxUE) {
689     }
690 }
691
692 static void
693 cs8900a_poll(struct eth_drv_sc *sc)
694 {
695     cyg_uint16 event;
696     cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
697     cyg_addrword_t base = cpd->base;
698     volatile int timeout=5000;
699
700     HAL_READ_UINT16(base+CS8900A_ISQ, event);
701     while (event != 0) {
702         switch (event & ISQ_EventMask) {
703         case ISQ_RxEvent:
704             cs8900a_RxEvent(sc, event);
705             break;
706         case ISQ_TxEvent:
707             cs8900a_TxEvent(sc, event);
708             break;
709         case ISQ_BufEvent:
710             cs8900a_BufEvent(sc, event);
711             break;
712         case ISQ_RxMissEvent:
713             // Receive miss counter has overflowed
714             break;
715         case ISQ_TxColEvent:
716             // Transmit collision counter has overflowed
717             break;
718         default:
719 #if DEBUG & 1
720             diag_printf("%s: Unknown event: %x\n", __FUNCTION__, event);
721 #endif
722             break;
723         }
724         while(timeout--);
725         timeout=5000;
726         HAL_READ_UINT16(base+CS8900A_ISQ, event);
727     }
728
729     CYGHWR_CL_CS8900A_PLF_INT_CLEAR(cpd);
730 }
731
732 #ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
733 void
734 cs8900a_fake_int(cyg_addrword_t param)
735 {
736     struct eth_drv_sc *sc = (struct eth_drv_sc *) param;
737     int s;
738
739 #if DEBUG & 1
740     diag_printf("cs8900a_fake_int()\n");
741 #endif
742
743     while (true) {
744         cyg_thread_delay(5);
745         s = splnet();
746         cs8900a_poll(sc);
747         splx(s);
748     }
749 }
750 #endif