]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/eth/ns/dp83816/v2_0/src/if_dp83816.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / devs / eth / ns / dp83816 / v2_0 / src / if_dp83816.c
1 //==========================================================================
2 //
3 //      dev/if_dp83816.c
4 //
5 //      Ethernet device driver for NS DP83816 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 // Copyright (C) 2003, 2004 Gary Thomas
13 // Copyright (C) 2004 eCosCentric Limited
14 //
15 // eCos is free software; you can redistribute it and/or modify it under
16 // the terms of the GNU General Public License as published by the Free
17 // Software Foundation; either version 2 or (at your option) any later version.
18 //
19 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
22 // for more details.
23 //
24 // You should have received a copy of the GNU General Public License along
25 // with eCos; if not, write to the Free Software Foundation, Inc.,
26 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 //
28 // As a special exception, if other files instantiate templates or use macros
29 // or inline functions from this file, or you compile this file and link it
30 // with other works to produce a work based on this file, this file does not
31 // by itself cause the resulting work to be covered by the GNU General Public
32 // License. However the source code for this file must still be made available
33 // in accordance with section (3) of the GNU General Public License.
34 //
35 // This exception does not invalidate any other reasons why a work based on
36 // this file might be covered by the GNU General Public License.
37 //
38 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39 // at http://sources.redhat.com/ecos/ecos-license/
40 // -------------------------------------------
41 //####ECOSGPLCOPYRIGHTEND####
42 //==========================================================================
43 //#####DESCRIPTIONBEGIN####
44 //
45 // Author(s):    gthomas
46 // Contributors: 
47 // Date:         2003-09-29
48 // Purpose:      
49 // Description:
50 //
51 //####DESCRIPTIONEND####
52 //
53 //==========================================================================
54
55 #include <pkgconf/system.h>
56 #include <pkgconf/io_eth_drivers.h>
57
58 #include <cyg/infra/cyg_type.h>
59 #include <cyg/hal/hal_arch.h>
60 #include <cyg/hal/hal_endian.h>
61 #include <cyg/infra/diag.h>
62 #include <cyg/hal/drv_api.h>
63 #include <cyg/hal/hal_if.h>
64 #include <cyg/io/eth/eth_drv.h>
65 #include <cyg/io/eth/netdev.h>
66 #include <string.h> // memcpy
67
68 #include "dp83816.h"
69 #include CYGDAT_DEVS_ETH_NS_DP83816_INL
70
71 #if DEBUG & 1
72 int norecurse;
73 #endif
74
75 #ifdef CYGHWR_NS_DP83816_USE_EEPROM
76 static cyg_uint16 dp83816_eeprom_read(struct dp83816_priv_data *dp, int location);
77 #endif
78
79 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
80 // This ISR is called when the ethernet interrupt occurs
81 static cyg_uint32
82 dp83816_isr(cyg_vector_t vector, cyg_addrword_t data)
83 {
84     struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
85     dp83816_priv_data_t *dp = (dp83816_priv_data_t *)sc->driver_private;
86
87     DEBUG_FUNCTION();
88
89     cyg_drv_interrupt_mask(dp->interrupt);
90     cyg_drv_interrupt_acknowledge(dp->interrupt);
91     return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
92 }
93 #endif // CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
94
95 // The deliver function (ex-DSR)  handles the ethernet [logical] processing
96 static void
97 dp83816_deliver(struct eth_drv_sc *sc)
98 {
99 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
100     dp83816_priv_data_t *dp = (dp83816_priv_data_t *)sc->driver_private;
101 #endif
102
103     DEBUG_FUNCTION();
104
105     // Service the interrupt:
106     dp83816_poll(sc);
107 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
108     // Allow interrupts to happen again
109     cyg_drv_interrupt_unmask(dp->interrupt);
110 #endif
111 }
112
113 static bool
114 dp83816_reset(dp83816_priv_data_t *dp)
115 {
116     unsigned char *bp;
117     dp83816_bd_t *bdp;
118     cyg_uint32 stat;
119     int i, timeout;
120
121     DP_OUT(dp->base, DP_CR, _CR_RST);  // Reset device
122     timeout = 10000;
123     do {
124         DP_IN(dp->base, DP_CR, stat);
125     } while (((stat & _CR_RST) != 0) && (--timeout > 0));
126     if (timeout == 0) {
127         diag_printf("DP83816 - reset timed out! - stat: %x\n", stat);
128         return false;
129     }
130
131     // Rx ring
132     bdp = dp->rxnext = CYGARC_UNCACHED_ADDRESS(dp->rxd);
133     bp = dp->rxbuf;
134     for (i = 0; i < dp->rxnum; i++, bdp++) {
135         bdp->next = (dp83816_bd_t *)CYG_CPU_TO_LE32(CYGARC_PHYSICAL_ADDRESS(bdp+1));
136         bdp->stat = CYG_CPU_TO_LE32(BD_INTR | _DP83816_BUFSIZE);  // Max buffer
137         bdp->buf = (unsigned char *)CYG_CPU_TO_LE32(CYGARC_PHYSICAL_ADDRESS(bp));
138         bp += _DP83816_BUFSIZE;
139     }
140     bdp--;  bdp->next = (dp83816_bd_t *)CYG_CPU_TO_LE32(CYGARC_PHYSICAL_ADDRESS(dp->rxd));
141     DP_OUT(dp->base, DP_RXCFG, _RXCFG_MXDMA_128 | ((64/32)<<_RXCFG_DRTH_SHIFT));
142     DP_OUT(dp->base, DP_RXDP, CYGARC_PHYSICAL_ADDRESS(dp->rxd));
143     // Tx ring
144     bdp = dp->txfill = dp->txint = CYGARC_UNCACHED_ADDRESS(dp->txd);
145     bp = dp->txbuf;
146     for (i = 0; i < dp->txnum; i++, bdp++) {
147         bdp->next = (dp83816_bd_t *)CYG_CPU_TO_LE32(CYGARC_PHYSICAL_ADDRESS(bdp+1));
148         bdp->stat = 0;  // Driver owns buffer for now
149         bdp->buf = (unsigned char *)CYG_CPU_TO_LE32(CYGARC_PHYSICAL_ADDRESS(bp));
150         bp += _DP83816_BUFSIZE;
151     }
152     bdp--;  bdp->next = (dp83816_bd_t *)CYG_CPU_TO_LE32(CYGARC_PHYSICAL_ADDRESS(dp->txd));
153     DP_OUT(dp->base, DP_TXCFG, _TXCFG_ATP |
154                                _TXCFG_MXDMA_128 |
155            /* _TXCFG_CSI | */
156                                ((256/32)<<_TXCFG_FLTH_SHIFT) |
157                                ((512/32)<<_TXCFG_DRTH_SHIFT));
158     DP_OUT(dp->base, DP_TXDP, CYGARC_PHYSICAL_ADDRESS(dp->txd));
159     dp->txbusy = 0;
160     // Fill in ESA
161     for (i = 0;  i < 6;  i+=2) {
162         DP_OUT(dp->base, DP_RFCR, i);
163         DP_OUT(dp->base, DP_RFDR, dp->enaddr[i] | (dp->enaddr[i+1]<<8));
164     }
165     // Setup up acceptance criteria
166     DP_OUT(dp->base, DP_RFCR, _RFCR_RFEN | _RFCR_AAB | _RFCR_APM);
167     // Set up interrupts
168     DP_IN(dp->base, DP_ISR, stat);  // Clear any current interrupts
169     DP_OUT(dp->base, DP_IMR, 0x00000000);  // Disable them all!
170     DP_OUT(dp->base, DP_IER, 0);
171     return true;
172 }
173
174 static bool 
175 dp83816_init(struct cyg_netdevtab_entry *tab)
176 {
177     struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
178     dp83816_priv_data_t *dp = (dp83816_priv_data_t *)sc->driver_private;
179     cyg_uint8 *base;
180     bool esa_ok = false;
181     unsigned char enaddr[6];
182
183     DEBUG_FUNCTION();
184
185     CYGHWR_NS_DP83816_PLF_INIT(dp);
186     base = dp->base;
187     if (!base) return false;  // No device found
188
189 #ifdef CYGPKG_REDBOOT
190 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
191     esa_ok = flash_get_config(dp->esa_key, enaddr, CONFIG_ESA);
192 #endif
193 #elif defined (CONFIG_ESA)
194     esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,         
195                                          dp->esa_key, enaddr, CONFIG_ESA);
196 #endif
197     // Get physical device address
198     // There are two different implementations due to parallel implementations.
199     // Both are included for backward compatibility, but
200     // the CYGHWR_DEVS_ETH_NS_DP83816_USE_EEPROM_ESA implementation is
201     // preferred simply because it is smaller.
202 #if defined(CYGHWR_DEVS_ETH_NS_DP83816_USE_EEPROM_ESA)
203     if (!esa_ok)
204     {
205         // Read the ESA from the PMATCH receive filter register, which
206         // will have been initialised from the EEPROM.
207         cyg_uint32 rfcrdat;
208         cyg_ucount8 i;
209         for (i = 0;  i < 6;  i+=2) {
210             DP_OUT(dp->base, DP_RFCR, i);
211             DP_IN(dp->base, DP_RFDR, rfcrdat );
212             enaddr[i] = rfcrdat & 0xff;
213             enaddr[i+1] = (rfcrdat>>8) & 0xff;
214         }
215         esa_ok = true;
216     }
217 #elif defined(CYGHWR_NS_DP83816_USE_EEPROM)
218     // This define (CYGHWR_NS_DP83816_USE_EEPROM) is deprecated.
219     {
220         cyg_uint16 t;
221
222         t = (dp83816_eeprom_read(dp, 0x0006) >> 15)
223             | (dp83816_eeprom_read(dp, 0x0007) << 1);
224         enaddr[0] = t & 0xFF;
225         enaddr[1] = t >> 8;
226         t = (dp83816_eeprom_read(dp, 0x0007) >> 15)
227             | (dp83816_eeprom_read(dp, 0x0008) << 1);
228         enaddr[2] = t & 0xFF;
229         enaddr[3] = t >> 8;
230         t = (dp83816_eeprom_read(dp, 0x0008) >> 15)
231             | (dp83816_eeprom_read(dp, 0x0009) << 1);
232         enaddr[4] = t & 0xFF;
233         enaddr[5] = t >> 8;
234         
235         esa_ok =  true;
236     }
237 #endif
238     if (esa_ok) {
239         memcpy(dp->enaddr, enaddr, sizeof(enaddr));
240     } else {
241         // Can't figure out ESA
242         diag_printf("DP83816 - Warning! ESA unknown\n");
243     }
244
245     //    DEBUG_FUNCTION();
246
247     if (!dp83816_reset(dp)) return false;
248
249 #if DEBUG & 8
250     diag_printf("DP83816 - ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
251                 dp->enaddr[0], dp->enaddr[1], dp->enaddr[2],
252                 dp->enaddr[3], dp->enaddr[4], dp->enaddr[5] );
253 #endif
254     
255 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
256     cyg_drv_interrupt_create(
257         dp->interrupt,
258         0,                       // Priority - unused
259         (cyg_addrword_t)sc,      // Data item passed to ISR & DSR
260         dp83816_isr,             // ISR
261         eth_drv_dsr,             // DSR
262         &dp->interrupt_handle,   // handle to intr obj
263         &dp->interrupt_object ); // space for int obj
264
265     cyg_drv_interrupt_attach(dp->interrupt_handle);
266     cyg_drv_interrupt_unmask(dp->interrupt);
267 #elif defined(CYGPKG_REDBOOT)
268     cyg_drv_interrupt_unmask(dp->interrupt);    
269 #endif
270
271     // Initialize upper level driver
272     (sc->funs->eth_drv->init)(sc, dp->enaddr);
273
274     return true;
275 }
276
277 static void
278 dp83816_stop(struct eth_drv_sc *sc)
279 {
280     dp83816_priv_data_t *dp = (dp83816_priv_data_t *)sc->driver_private;
281
282     DP_OUT(dp->base, DP_IMR, 0x00000000);  // Disable interrupts
283     DP_OUT(dp->base, DP_IER, 0);
284     DP_OUT(dp->base, DP_CR, _CR_RXD | _CR_TXD);
285 }
286
287 //
288 // This function is called to "start up" the interface.  It may be called
289 // multiple times, even when the hardware is already running.  It will be
290 // called whenever something "hardware oriented" changes and should leave
291 // the hardware ready to send/receive packets.
292 //
293 static void
294 dp83816_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
295 {
296     dp83816_priv_data_t *dp = (dp83816_priv_data_t *)sc->driver_private;
297
298     DP_OUT(dp->base, DP_IMR, 0xFFFFFFFF);  // Enable interrupts
299     DP_OUT(dp->base, DP_IER, 1);
300     DP_OUT(dp->base, DP_CR, _CR_RXE | _CR_TXE);
301 }
302
303 //
304 // This routine is called to perform special "control" opertions
305 //
306 static int
307 dp83816_control(struct eth_drv_sc *sc, unsigned long key,
308                void *data, int data_len)
309 {
310     switch (key) {
311     case ETH_DRV_SET_MAC_ADDRESS:
312         return 0;
313         break;
314     default:
315         return 1;
316         break;
317     }
318 }
319
320 //
321 // This routine is called to see if it is possible to send another packet.
322 // It will return non-zero if a transmit is possible, zero otherwise.
323 //
324 static int
325 dp83816_can_send(struct eth_drv_sc *sc)
326 {
327     dp83816_priv_data_t *dp = (dp83816_priv_data_t *)sc->driver_private;
328
329     DEBUG_FUNCTION();
330     return (dp->txnum - dp->txbusy);
331 }
332
333 //
334 // This routine is called to send data to the hardware.  It is known a-priori
335 // that there is free buffer space (dp->tx_next).
336 //
337 static void 
338 dp83816_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
339             int total_len, unsigned long key)
340 {
341     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
342     int i, len;
343     unsigned char *data;
344     dp83816_bd_t *bdp;
345 #if 0
346     cyg_uint32 ints;
347     cyg_drv_dsr_lock();
348     HAL_DISABLE_INTERRUPTS(ints);
349 #endif
350     
351     bdp= dp->txfill;
352
353     DEBUG_FUNCTION();
354
355     len = total_len;
356     if (len < IEEE_8023_MIN_FRAME) len = IEEE_8023_MIN_FRAME;
357     data = (unsigned char *)CYGARC_VIRTUAL_ADDRESS(CYG_LE32_TO_CPU((unsigned long)bdp->buf));
358 #if DEBUG & 1
359     if (!norecurse) {
360         norecurse=1;
361         diag_printf("send sg_len==%d, txbusy=%d, len=%d, total_len=%d\n", sg_len, dp->txbusy, len, total_len);
362         norecurse = 0;
363     }
364 #endif
365     for (i = 0;  i < sg_len;  i++) {
366         memcpy(data, (unsigned char *)sg_list[i].buf, sg_list[i].len);
367         data += sg_list[i].len;
368     }
369     bdp->key = key;
370     bdp->stat = CYG_CPU_TO_LE32(len | BD_OWN | BD_INTR);
371     dp->txbusy++;
372     bdp = (dp83816_bd_t *)CYGARC_UNCACHED_ADDRESS(CYGARC_VIRTUAL_ADDRESS(CYG_LE32_TO_CPU((unsigned long)bdp->next)));
373     dp->txfill = bdp;
374     // Kick the device, in case it went idle
375     DP_OUT(dp->base, DP_CR, _CR_TXE);
376 #if 0
377     cyg_drv_dsr_unlock();
378     HAL_RESTORE_INTERRUPTS(ints);
379 #endif
380 }
381
382 static void
383 dp83816_TxEvent(struct eth_drv_sc *sc)
384 {
385     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
386     dp83816_bd_t *bdp = dp->txint;
387
388     DEBUG_FUNCTION();
389     while ((CYG_LE32_TO_CPU(bdp->stat) & (BD_OWN|BD_INTR)) == BD_INTR) {
390         // Tell higher level we sent this packet
391         (sc->funs->eth_drv->tx_done)(sc, bdp->key, 0);
392         bdp->stat = 0;  // retake buffer
393         bdp->key = 0;
394         dp->txbusy--;
395         bdp = (dp83816_bd_t *)CYGARC_UNCACHED_ADDRESS(CYGARC_VIRTUAL_ADDRESS(CYG_LE32_TO_CPU((unsigned long)bdp->next)));
396     }
397     dp->txint = bdp;
398 }
399
400 //
401 // This function is called when a packet has been received.  It's job is
402 // to prepare to unload the packet from the hardware.  Once the length of
403 // the packet is known, the upper layer of the driver can be told.  When
404 // the upper layer is ready to unload the packet, the internal function
405 // 'dp83816_recv' will be called to actually fetch it from the hardware.
406 //
407 static void
408 dp83816_RxEvent(struct eth_drv_sc *sc)
409 {
410     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
411     dp83816_bd_t *bdp = CYGARC_UNCACHED_ADDRESS(dp->rxd);
412     dp83816_bd_t *bdfirst = CYGARC_UNCACHED_ADDRESS(dp->rxd);
413     int len, err;
414
415     DEBUG_FUNCTION();
416
417     while (true) {
418         if ((CYG_LE32_TO_CPU(bdp->stat) & BD_OWN) != 0) {
419             err = CYG_LE32_TO_CPU(bdp->stat) & (BD_RXA|BD_RXO|BD_LONG|BD_RUNT|BD_ISE|BD_CRCE|BD_FAE|BD_COL);
420             if (err != 0) {
421                 diag_printf("RxError: %x\n", err);
422             }
423             len = CYG_LE32_TO_CPU(bdp->stat) & BD_LENGTH_MASK;
424             dp->rxnext = bdp;
425             (sc->funs->eth_drv->recv)(sc, len);
426             bdp->stat = CYG_CPU_TO_LE32(BD_INTR | _DP83816_BUFSIZE);  // Give back buffer
427         }
428         bdp = (dp83816_bd_t *)CYGARC_UNCACHED_ADDRESS(CYGARC_VIRTUAL_ADDRESS(CYG_LE32_TO_CPU((unsigned long)bdp->next)));
429         if (bdp == bdfirst) {
430             break;
431         }
432     }
433 }
434
435 //
436 // This function is called as a result of the "eth_drv_recv()" call above.
437 // It's job is to actually fetch data for a packet from the hardware once
438 // memory buffers have been allocated for the packet.  Note that the buffers
439 // may come in pieces, using a scatter-gather list.  This allows for more
440 // efficient processing in the upper layers of the stack.
441 //
442 static void
443 dp83816_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
444 {
445     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
446     dp83816_bd_t *bdp = dp->rxnext;
447     unsigned char *data;
448     int i;
449
450     data = (unsigned char *)CYGARC_VIRTUAL_ADDRESS(CYG_LE32_TO_CPU((unsigned long)bdp->buf));
451     for (i = 0;  i < sg_len;  i++) {
452         if( sg_list[i].buf )
453             memcpy((void *)sg_list[i].buf, data, sg_list[i].len);
454         data += sg_list[i].len;
455     }
456 }
457
458 static void
459 dp83816_warm_reset(struct eth_drv_sc *sc)
460 {
461     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
462     dp83816_bd_t *bdp;
463     int i;
464
465     DEBUG_FUNCTION();
466     // Free up any active Tx buffers
467     bdp = CYGARC_UNCACHED_ADDRESS(dp->txd);
468     for (i = 0; i < dp->txnum; i++, bdp++) {
469         if (bdp->key) {
470             (sc->funs->eth_drv->tx_done)(sc, bdp->key, 0);
471         }
472     }
473     // Reset the device
474     dp83816_reset(dp);
475     DP_OUT(dp->base, DP_CR, _CR_RXE | _CR_TXE);
476 }
477
478 static void
479 dp83816_poll(struct eth_drv_sc *sc)
480 {
481     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
482     unsigned long stat, cr_stat;
483
484 #if defined(CYGPKG_REDBOOT)
485     cyg_drv_interrupt_acknowledge(dp->interrupt);
486 #endif
487     
488     DP_IN(dp->base, DP_ISR, stat);
489     do {
490         if ((stat & (_ISR_TXDESC|_ISR_TXOK)) != 0) {
491             dp83816_TxEvent(sc);
492         }
493         if ((stat & (_ISR_RXDESC|_ISR_RXOK|_ISR_RXERR)) != 0) {
494             dp83816_RxEvent(sc);
495         }
496         DP_IN(dp->base, DP_CR, cr_stat);
497         if ((stat & (_ISR_HIBERR|_ISR_TXURN|_ISR_RXORN)) != 0) {            
498 #if DEBUG & 2
499             diag_printf("DP83816 - major error: %x, cmd_stat: %x\n", stat, cr_stat);
500 #endif
501             // Try to reset the device
502             dp83816_warm_reset(sc);
503         }
504 #if 0
505         if (((cr_stat & _CR_RXE) == 0) ||
506             ((dp->txbusy > 1) && ((cr_stat & _CR_TXE) == 0)))
507         {
508 #if DEBUG & 2
509             // What happened?
510             diag_printf("DP83816 went to lunch? - stat: %x/%x, txbusy: %x, bdstat: %x\n", cr_stat, stat, dp->txbusy, dp->txint->stat);
511 #endif
512             // Try to reset the device
513             dp83816_warm_reset(sc);
514         }
515 #endif
516         DP_IN(dp->base, DP_ISR, stat);
517     } while (stat != 0);
518 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
519     CYGHWR_NS_DP83816_PLF_INT_CLEAR(dp);
520 #endif
521 }
522
523 static int
524 dp83816_int_vector(struct eth_drv_sc *sc)
525 {
526     struct dp83816_priv_data *dp = (struct dp83816_priv_data *)sc->driver_private;
527     return dp->interrupt;
528 }
529
530 /* EEPROM Functions */
531 #ifdef CYGHWR_NS_DP83816_USE_EEPROM
532
533 #define EEPROM_READ(dp, x)  DP_IN((dp)->base, DP_MEAR, (x))
534 #define EEPROM_WRITE(dp, x) DP_OUT((dp)->base, DP_MEAR, (x))
535 #define EEPROM_DELAY(dp)    CYG_MACRO_START cyg_uint16 t; EEPROM_READ((dp), t); CYG_MACRO_END
536
537 #define DP83816_EEPROM_ADDR_LEN  6
538 #define DP83816_EE_READ_CMD     (6 << DP83816_EEPROM_ADDR_LEN)
539
540
541 /* EEPROM data is bit-swapped. */
542 static cyg_uint16 dp83816_eeprom_fixup_data(cyg_uint16 input)
543 {
544     cyg_uint16 output = 0;
545     int i;
546
547     for (i = 0; i < 16; i++) {
548         output = (output << 1) | (input & 0x0001);
549         input >>= 1;
550     }
551     return output;
552 }
553
554 static cyg_uint16 dp83816_eeprom_command(struct dp83816_priv_data *dp, int cmd, int cmd_len)
555 {
556     int d = 0;
557
558     EEPROM_WRITE(dp, _MEAR_EESEL);
559
560     do {
561         cyg_uint32 c = (cmd & (1 << cmd_len)) ? _MEAR_EEDI : 0;
562         cyg_uint8 t;
563
564         EEPROM_WRITE(dp, c | _MEAR_EESEL);
565         EEPROM_DELAY(dp);
566         EEPROM_WRITE(dp, c | _MEAR_EESEL | _MEAR_EECLK);
567         EEPROM_DELAY(dp);
568
569         EEPROM_READ(dp, t);
570         d <<= 1;
571         d |= (t & _MEAR_EEDO) ? 1 : 0;
572     } while (cmd_len--);
573
574     EEPROM_WRITE(dp, _MEAR_EESEL);
575     EEPROM_WRITE(dp, 0);
576
577     return d & 0xffff;
578 }
579
580 static cyg_uint16 dp83816_eeprom_read(struct dp83816_priv_data *dp, int loc)
581 {
582     cyg_uint16 d;
583
584     d = dp83816_eeprom_command(dp, (loc | DP83816_EE_READ_CMD) << 16,
585                                3 + DP83816_EEPROM_ADDR_LEN + 16);
586
587     return dp83816_eeprom_fixup_data(d);
588 }
589
590 #endif /* CYGHWR_NS_DP83816_USE_EEPROM */