]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/eth/fec/v2_0/src/if_fec.c
Initial revision
[karo-tx-redboot.git] / packages / devs / eth / fec / v2_0 / src / if_fec.c
1 //==========================================================================
2 //
3 //      dev/if_fec.c
4 //
5 //      Device driver for FEC 
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):    Fred Fan
54 // Contributors: 
55 // Date:         2006-08-23
56 // Purpose:      
57 // Description:  Driver for FEC ethernet controller
58 //
59 // Note:         
60 //
61 //####DESCRIPTIONEND####
62 //
63 //==========================================================================
64
65 #include <pkgconf/system.h>
66 #ifdef CYGPKG_KERNEL
67 #include <cyg/kernel/kapi.h>
68 #endif
69 #include <pkgconf/io_eth_drivers.h>
70 #include <pkgconf/devs_eth_fec.h>
71
72 #include <cyg/infra/cyg_type.h>
73 #include <cyg/infra/cyg_ass.h>
74 #include <cyg/hal/hal_arch.h>
75 #include <cyg/hal/hal_intr.h>
76 #include <cyg/hal/hal_endian.h>
77 #include <cyg/infra/diag.h>
78 #include <cyg/hal/drv_api.h>
79 #include <cyg/hal/hal_soc.h>
80 #undef __ECOS
81 #define __ECOS
82 #include <cyg/io/eth/eth_drv.h>
83 #include <cyg/io/eth/netdev.h>
84
85 #ifdef CYGPKG_DEVS_ETH_PHY
86 /* generic PHY device access functions */
87 void mxc_fec_phy_init(void);
88 void mxc_fec_phy_reset(void);
89 bool mxc_fec_phy_read(int reg, int unit, unsigned short *data);
90 void mxc_fec_phy_write(int reg, int unit, unsigned short data);
91
92 #include <cyg/io/eth_phy.h>
93 #endif
94
95 #include <cyg/io/fec.h>
96 #define __WANT_DEVS
97 #include CYGDAT_DEVS_ETH_FEC_INL
98 #undef __WANT_DEVS
99
100 #include <redboot.h>
101
102 #include <cyg/hal/hal_mm.h>
103 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
104 #include <flash_config.h>
105 #endif
106
107
108 #define MII_REG_CR          0  /* Control Register                         */
109 #define MII_REG_SR          1  /* Status Register                          */
110 #define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
111 #define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
112
113 static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool show);
114
115 #ifndef CYGPKG_DEVS_ETH_PHY
116 /*!
117  * Global variable which contains the name of FEC driver and device. 
118  */
119 static char  mxc_fec_name[] = "mxc_fec";
120
121 /*!
122  * Global variable which defines the private structure of FEC device.
123  */
124 static mxc_fec_priv_t  mxc_fec_private ;
125 #endif
126
127 /*!
128  *Global variable which defines the buffer descriptions for receiving frame
129  *      comment:: it must aligned by 128-bits.
130  */
131 static mxc_fec_bd_t mxc_fec_rx_bd[FEC_BD_RX_NUM] __attribute__ ( ( aligned(32) ) ) ;
132
133 /*!
134  *Global variable which defines the buffer descriptions for receiving frame
135  *      comment:: it must aligned by 128-bits.
136  */
137 static mxc_fec_bd_t mxc_fec_tx_bd[FEC_BD_TX_NUM] __attribute__ ( ( aligned(32) ) ) ;
138
139 /*!
140  * Global variable which contains the frame buffers , 
141  */
142 static unsigned char mxc_fec_rx_buf[FEC_BD_RX_NUM][2048] __attribute__ ( ( aligned(32) ) ) ;
143
144 /*!
145  * Global variable which contains the frame buffers , 
146  */
147 static unsigned char mxc_fec_tx_buf[FEC_BD_TX_NUM][2048] __attribute__ ( ( aligned(32) ) ) ;
148
149
150 /*!
151  * This function get the value of  PHY registers by MII interface
152  */
153 static int
154 mxc_fec_mii_read(volatile mxc_fec_reg_t *hw_reg, unsigned char phy_addr, unsigned char reg_addr,
155                  unsigned short int *value)
156 {
157         unsigned long waiting = FEC_MII_TIMEOUT;
158         
159         if (net_debug) diag_printf("%s: Trying to read phy[%02x] reg %04x\n",
160                                    __FUNCTION__, phy_addr, reg_addr);
161         if (hw_reg->eir & FEC_EVENT_MII) {
162                 if (net_debug) diag_printf("%s: Clearing EIR_EVENT_MII\n", __FUNCTION__);
163                 hw_reg->eir = FEC_EVENT_MII ;
164         }
165         if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, hw_reg->eir);
166         hw_reg->mmfr = FEC_MII_READ(phy_addr, reg_addr);/*Write CMD*/
167         while (1) {
168                 if (hw_reg->eir & FEC_EVENT_MII) {
169                         if (net_debug) diag_printf("%s: Got EIR_EVENT_MII: EIR=%08lx\n",
170                                                    __FUNCTION__, hw_reg->eir);
171                         hw_reg->eir = FEC_EVENT_MII ; 
172                         break;
173                 }
174                 if (--waiting == 0) {
175                         diag_printf("%s: Read from PHY at addr %d reg 0x%02x timed out: EIR=%08lx\n",
176                                     __FUNCTION__, phy_addr, reg_addr, hw_reg->eir);
177                         return -1;
178                 }
179                 hal_delay_us(FEC_MII_TICK);     
180         }
181         *value = FEC_MII_GET_DATA(hw_reg->mmfr);
182         if (net_debug) diag_printf("%s: Read %04x from phy[%02x] reg %04x\n", __FUNCTION__,
183                                    *value, phy_addr, reg_addr);
184         return 0;
185 }
186
187 /*!
188  * This function set the value of  PHY registers by MII interface
189  */
190 static int 
191 mxc_fec_mii_write(volatile mxc_fec_reg_t * hw_reg, unsigned char phy_addr, unsigned char reg_addr,
192                   unsigned short int value)
193 {
194         unsigned long waiting = FEC_MII_TIMEOUT;
195         
196         if (net_debug) diag_printf("%s: Trying to write %04x to phy[%02x] reg %04x\n", __FUNCTION__,
197                                    value, phy_addr, reg_addr);
198         if(hw_reg->eir & FEC_EVENT_MII ) {
199                 if (net_debug) diag_printf("%s: Clearing EIR_EVENT_MII\n", __FUNCTION__);
200                 hw_reg->eir = FEC_EVENT_MII;
201         }
202         if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, hw_reg->eir);
203         hw_reg->mmfr = FEC_MII_WRITE(phy_addr, reg_addr, value);/*Write CMD*/
204         if (net_debug) diag_printf("%s: Wrote cmd %08x to MMFR\n", __FUNCTION__,
205                                    FEC_MII_WRITE(phy_addr, reg_addr, value));
206         while (1) {
207                 if (hw_reg->eir & FEC_EVENT_MII) {
208                         if (net_debug) diag_printf("%s: Got EIR_EVENT_MII: EIR=%08lx\n",
209                                                    __FUNCTION__, hw_reg->eir);
210                         hw_reg->eir = FEC_EVENT_MII ; 
211                         break;
212                 }
213                 if (--waiting == 0) {
214                         diag_printf("%s: Write to PHY at addr %d reg 0x%02x timed out: EIR=%08lx\n",
215                                     __FUNCTION__, phy_addr, reg_addr, hw_reg->eir);
216                         return -1;
217                 }
218                 hal_delay_us(FEC_MII_TICK);
219         }
220         if (net_debug) diag_printf("%s: Write to phy register succeeded\n", __FUNCTION__);
221         return 0;
222 }
223
224 static void
225 mxc_fec_set_mac_address(volatile mxc_fec_reg_t *dev, unsigned char *enaddr)
226 {
227         unsigned long value;
228         
229         value = enaddr[0];
230         value = (value << 8) + enaddr[1];
231         value = (value << 8) + enaddr[2];
232         value = (value << 8) + enaddr[3];
233         dev->palr = value;
234         
235         value = enaddr[4];
236         value = (value << 8) + enaddr[5];
237         dev->paur = (value << 16);
238 }
239
240 /*!
241  * This function set the value of  PHY registers by MII interface
242  */
243 static void 
244 mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
245 {
246         mxc_fec_priv_t *priv = sc?sc->driver_private:NULL;
247         volatile mxc_fec_reg_t *chip = priv?priv->hw_reg:NULL;
248
249         if (!(priv && chip) || enaddr == NULL ) {
250                 diag_printf("BUG[start]: MAC address or some fields in driver is NULL\n");
251                 return;
252         }
253         mxc_fec_set_mac_address(chip, enaddr);
254
255         priv->tx_busy = 0;
256         chip->ecr |= FEC_ETHER_EN;
257         chip->rdar |= FEC_RX_TX_ACTIVE;
258 }
259
260 /*!
261  * This function pauses the FEC controller.
262  */
263 static void 
264 mxc_fec_stop(struct eth_drv_sc *sc)
265 {
266         mxc_fec_priv_t *priv = sc?sc->driver_private:NULL;
267         volatile mxc_fec_reg_t *chip = priv?priv->hw_reg:NULL;
268         if (!(priv && chip)) {
269                 diag_printf("BUG[stop]: some fields in driver is NULL\n");
270                 return;
271         }
272         chip->ecr &= ~FEC_ETHER_EN;
273 }
274
275 static int  
276 mxc_fec_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length)
277 {
278         /*TODO:: Add support */
279         diag_printf("mxc_fec_control: key=0x%08lx, data=%p, data_len=0x%08x\n",
280                     key, data, data_length);
281         return 0;
282 }
283
284 /*!
285  * This function checks the status of FEC control.
286  */
287 static int  
288 mxc_fec_can_send(struct eth_drv_sc *sc)
289 {
290         mxc_fec_priv_t *priv = sc?sc->driver_private:NULL;
291         volatile mxc_fec_reg_t *hw_reg = priv?priv->hw_reg:NULL;
292
293         if (!(priv && hw_reg)) {
294                 diag_printf("BUG[can_send]:the private pointer and register pointer in MXC_FEC is NULL\n");
295                 return 0;
296         }
297         if (priv->tx_busy) {
298                 diag_printf("WARNING[can_send]: MXC_FEC is busy for transmittinig\n");
299                 return 0;
300         }
301
302         if (!(hw_reg->ecr & FEC_ETHER_EN)) {
303                 diag_printf("WARNING[can_send]: MXC_FEC is not enabled\n");
304                 return 0;
305         }
306
307         if (hw_reg->tcr & FEC_TCR_RFC_PAUSE) {
308                 diag_printf("WARNING[can_send]: MXC_FEC is paused\n");
309                 return 0;
310         }
311
312         if (!(priv->status & FEC_STATUS_LINK_ON)) {
313                 /* Reading the PHY status for every packet to be sent is
314                  * a real performance killer.
315                  * Thus, only read the PHY status when the link is down to
316                  * detect a possible new connection
317                  */
318 #ifdef CYGPKG_DEVS_ETH_PHY
319                 unsigned short value;
320                 value = _eth_phy_state(priv->phy);
321 #else
322                 unsigned short value;
323                 mxc_fec_mii_read(hw_reg, priv->phy_addr, 1, &value);
324 #endif
325                 if (value & PHY_STATUS_LINK_ST) {
326                         if (!(priv->status & FEC_STATUS_LINK_ON)) {
327                                 mxc_fec_phy_status(priv, value, true);
328                         }
329                 } else {
330                         if (priv->status & FEC_STATUS_LINK_ON) {
331                                 mxc_fec_phy_status(priv, value, true);
332                         }
333                 }
334         }
335
336         return priv->status & FEC_STATUS_LINK_ON;
337 }
338
339 /*!
340  * This function transmits a frame.
341  */
342 static void 
343 mxc_fec_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total,
344              unsigned long key)
345 {
346         mxc_fec_priv_t *dev = sc?sc->driver_private:NULL;
347         volatile mxc_fec_reg_t *hw_reg = dev?dev->hw_reg:NULL;
348         mxc_fec_bd_t *p;
349         int i, off;
350
351         if ( dev == NULL || hw_reg == NULL) {
352                 diag_printf("BUG[TX]: some fields in driver are NULL\n");
353                 return;
354         }
355         if ( total > (FEC_FRAME_LEN-4)) total = FEC_FRAME_LEN-4;
356         if ( sg_list == NULL || total <= 14 ) {
357                 if(sc->funs->eth_drv && sc->funs->eth_drv->tx_done) {
358                         sc->funs->eth_drv->tx_done(sc, key, -1);
359                 }
360                 return;
361         }       
362
363         for(i=0, off=0, p = dev->tx_cur; i<sg_len; i++) {
364                 unsigned long vaddr;
365                 if(p->status & BD_TX_ST_RDY) {
366                         diag_printf("BUG[TX]:MXC_FEC's status=%x\n", p->status);
367                         break;
368                 }
369                 if (sg_list[i].buf == 0) {
370                         diag_printf("WARNING[TX]: sg_list->buf is NULL\n");
371                         break;
372                 }
373                 vaddr = hal_ioremap_nocache((unsigned long)p->data) + off;
374                 memcpy((void *)vaddr, (void *)sg_list[i].buf, sg_list[i].len);
375                 off += sg_list[i].len;
376         }
377         if ( off < 14 ) {
378                 diag_printf("WARNING[TX]: data len is %d\n", off);
379                 return;
380         }
381         p->length = off; 
382         p->status &= ~(BD_TX_ST_LAST|BD_TX_ST_RDY|BD_TX_ST_TC|BD_TX_ST_ABC);
383         p->status |= BD_TX_ST_LAST| BD_TX_ST_RDY | BD_TX_ST_TC;
384         if(p->status & BD_TX_ST_WRAP ) {
385                 p = dev->tx_bd;
386         } else p++;
387         dev->tx_cur = p;
388         dev->tx_busy = 1;
389         dev->tx_key = key;
390         hw_reg->tdar = FEC_RX_TX_ACTIVE;        
391 }
392
393 /*!
394  * This function receives ready Frame in DB.
395  */
396 static void 
397 mxc_fec_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
398 {
399         mxc_fec_priv_t * priv = sc?sc->driver_private:NULL;
400         mxc_fec_bd_t * p;
401         unsigned long vaddr;
402         if(sg_list == NULL || priv == NULL || sg_len <= 0) {
403                 diag_printf("BUG[RX]: driver's private field or argument of this calling is NULL \n");
404                 return;
405         }
406         
407         /*TODO: I think if buf pointer is NULL, this function 
408          * should not be called
409          */
410         if(sg_list->buf == 0) {
411                 diag_printf("WARING[RX]: the sg_list is empty\n");
412                 return;
413         }
414         p = priv->rx_cur;
415         
416         if(p->status & BD_RX_ST_EMPTY) {
417                 diag_printf("BUG[RX]: status =%x\n", p->status);
418                 return;
419         }
420
421         if(!(p->status & BD_RX_ST_LAST)) {
422                 diag_printf("BUG[RX]: status =%x\n", p->status);
423                 return;
424         }
425         vaddr = hal_ioremap_nocache((unsigned long)p->data);
426         /*TODO::D_CACHE invalid this data buffer*/
427         memcpy((void *)sg_list->buf, (void *)vaddr, p->length -4);
428 }
429
430 static void 
431 mxc_fec_deliver(struct eth_drv_sc *sc)
432 {
433         /*TODO::When redboot support thread , 
434          *      the polling function will be called at here
435          */
436         return;
437 }
438
439 static void 
440 mxc_fec_check_rx_bd(struct eth_drv_sc * sc)
441 {
442         /* This funtion just called by polling funtion*/
443         mxc_fec_priv_t * priv = sc->driver_private;
444         mxc_fec_bd_t * p;
445         volatile mxc_fec_reg_t * hw_reg = priv->hw_reg;
446         int i;
447         
448         for(i = 0, p = priv->rx_cur; i< FEC_RX_FRAMES; i++){
449                 /*TODO::D-CACHE invalid this BD.
450                 *In WRITE_BACK mode: this maybe destroy the next BD 
451                 *       when the CACHE_LINE write back.
452                 */
453                 if(p->status & BD_RX_ST_EMPTY) {
454                         break;
455                 }
456                 if(!(p->status & BD_RX_ST_LAST)) {
457                         diag_printf("BUG[RX]: status=%x, length=%x\n", p->status, p->length);
458                         goto skip_next;
459                 }
460                 
461                 if((p->status & BD_RX_ST_ERRS)|| (p->length > FEC_FRAME_LEN)) {
462                         diag_printf("BUG[RX]: status=%x, length=%x\n", p->status, p->length);
463                 } else {
464                         sc->funs->eth_drv->recv(sc, p->length -4);
465                 }
466 skip_next:
467                 p->status = (p->status & BD_RX_ST_WRAP) | BD_RX_ST_EMPTY;
468                 
469                 if ( p->status & BD_RX_ST_WRAP) {
470                         p = priv->rx_bd;
471                 } else {
472                         p++;
473                 } 
474                 priv->rx_cur = p;       
475                 hw_reg->ecr |= FEC_ETHER_EN;
476                 hw_reg->rdar |= FEC_RX_TX_ACTIVE;
477         }
478 }
479
480 /*!
481  * This function checks the event of FEC controller
482  */
483 static void 
484 mxc_fec_poll(struct eth_drv_sc * sc)
485 {
486         mxc_fec_priv_t * priv = sc?sc->driver_private:NULL;
487         volatile mxc_fec_reg_t * hw_reg = priv?priv->hw_reg:NULL;
488         unsigned long value;
489
490         if ( priv == NULL || hw_reg == NULL) {
491                 diag_printf("BUG[POLL]: some fields in driver are NULL\n");
492                 return;
493         }
494         value = hw_reg->eir;
495         hw_reg->eir = value & (~FEC_EVENT_MII);
496         
497         if(value&FEC_EVENT_TX_ERR) {
498                 diag_printf("WARNING[POLL]: There are errors(%lu) for transmit\n",
499                             value&FEC_EVENT_TX_ERR);
500                 sc->funs->eth_drv->tx_done(sc, priv->tx_key, -1);
501                 priv->tx_busy = 0;
502         } else {
503                 if(value&FEC_EVENT_TX) {
504                         sc->funs->eth_drv->tx_done(sc,  priv->tx_key, 0);
505                         priv->tx_busy = 0;
506                 }
507         }
508         
509         if(value&FEC_EVENT_RX) {
510                 mxc_fec_check_rx_bd(sc);
511         }
512
513         if(value & FEC_EVENT_HBERR) {
514                 diag_printf("WARNGING[POLL]: Hearbeat error!\n");
515         }
516
517         if(value & FEC_EVENT_EBERR) {
518                 diag_printf("WARNING[POLL]: Ethernet Bus Error!\n");
519         }
520 }
521
522
523 static int
524 mxc_fec_int_vector(struct eth_drv_sc *sc)
525 {
526         /*TODO::
527          *      get FEC interrupt number        
528          */
529         return -1;
530 }
531
532 /*!
533  * The function initializes the description buffer for receiving or transmitting
534  */
535 static void
536 mxc_fec_bd_init(mxc_fec_priv_t * dev)
537 {
538         int i;
539         mxc_fec_bd_t * p;
540
541         p = dev->rx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_rx_bd));
542         for(i=0; i<FEC_BD_RX_NUM; i++, p++){
543                 p->status = BD_RX_ST_EMPTY;
544                 p->length = 0;
545                 p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_rx_buf[i]);
546         }
547
548         dev->rx_bd[i-1].status |= BD_RX_ST_WRAP;
549         dev->rx_cur = dev->rx_bd;
550
551         p = dev->tx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_tx_bd));
552         for(i=0; i<FEC_BD_TX_NUM; i++, p++){
553                 p->status = 0;
554                 p->length = 0;
555                 p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_tx_buf[i]);
556         }
557
558         dev->tx_bd[i-1].status |= BD_TX_ST_WRAP;
559         dev->tx_cur = dev->tx_bd;
560         
561         /*TODO:: add the sync function for items*/
562 }
563
564 /*!
565  *This function initializes FEC controller. 
566  */
567 static void 
568 mxc_fec_chip_init(mxc_fec_priv_t * dev)
569 {
570         volatile mxc_fec_reg_t * chip = dev->hw_reg;
571         unsigned long ipg_clk;
572
573         chip->ecr = FEC_RESET;
574         while(chip->ecr & FEC_RESET) {
575                 hal_delay_us(FEC_COMMON_TICK);
576         }
577
578         chip->eimr = 0x00000000;
579         chip->eir = 0xFFFFFFFF;
580         
581         chip->rcr = (chip->rcr&~(0x0000003F))|FEC_RCR_FCE|FEC_RCR_MII_MODE;
582         chip->tcr |= FEC_TCR_FDEN;
583         chip->mibc |= FEC_MIB_DISABLE;
584         
585         chip->iaur = 0;
586         chip->ialr = 0;
587         chip->gaur = 0;
588         chip->galr = 0;
589
590         /*TODO:: Use MII_SPEED(IPG_CLK) to get the value*/ 
591         ipg_clk = get_main_clock(IPG_CLK);
592         
593         chip->mscr = (chip->mscr & 0x7e) | (((ipg_clk + 499999) / 2500000 / 2) << 1);
594         if (net_debug) diag_printf("mscr set to %08lx for ipg_clk %ld\n", chip->mscr,
595                                    ipg_clk);
596         /*Enable ETHER_EN*/
597         chip->emrbr = 2048-16;
598         chip->erdsr = hal_virt_to_phy((unsigned long)dev->rx_bd);
599         chip->etdsr = hal_virt_to_phy((unsigned long)dev->tx_bd);
600 }
601
602 static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool show)
603 {
604 #ifdef CYGPKG_DEVS_ETH_PHY
605         if (value & ETH_PHY_STAT_LINK) {
606                 dev->status |= FEC_STATUS_LINK_ON;
607                 if (value & ETH_PHY_STAT_FDX) {
608                         dev->status |= FEC_STATUS_FULL_DPLX;
609                 } else {
610                         dev->status &= ~FEC_STATUS_FULL_DPLX;
611                 }
612                 if (value & ETH_PHY_STAT_100MB) {
613                         dev->status |= FEC_STATUS_100M;
614                 } else {
615                         dev->status &= ~FEC_STATUS_100M;
616                 }
617         } else {
618                 dev->status &= ~FEC_STATUS_LINK_ON;
619         }
620 #else
621         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value);
622         if (value & PHY_STATUS_LINK_ST) {
623                 dev->status |= FEC_STATUS_LINK_ON;
624         } else {
625                 dev->status &= ~FEC_STATUS_LINK_ON;
626         }
627
628         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_DIAG_REG, &value);
629         if (value & PHY_DIAG_DPLX) {
630                 dev->status |= FEC_STATUS_FULL_DPLX;
631         } else {
632                 dev->status &= ~FEC_STATUS_FULL_DPLX;
633         }
634         if (value & PHY_DIAG_RATE) {
635                 dev->status |= FEC_STATUS_100M;
636         } else {
637                 dev->status &= ~FEC_STATUS_100M;
638         }
639 #endif
640         if (!show) {
641                 return;
642         }
643         if (dev->status & FEC_STATUS_LINK_ON) {
644                 diag_printf("FEC: [ %s ] [ %s ]:\n", 
645                             (dev->status & FEC_STATUS_FULL_DPLX) ? "FULL_DUPLEX" : "HALF_DUPLEX",
646                             (dev->status & FEC_STATUS_100M) ? "100 Mbps" : "10 Mbps");
647         } else {
648                 diag_printf("FEC: no cable\n");
649         }
650 }
651
652 #ifndef CYGPKG_DEVS_ETH_PHY
653 /*!
654  * This function initialize PHY
655  */
656 static bool
657 mxc_fec_phy_init(mxc_fec_priv_t *dev)
658 {
659         unsigned short value = 0;
660         unsigned long timeout=FEC_COMMON_TIMEOUT;
661         /*Reset PHY*/
662         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, PHY_CTRL_RESET);
663         while (timeout--) {
664                 if (mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, &value)) {
665                         return false;
666                 }
667
668                 if(!(value & PHY_CTRL_RESET)) {
669                         if (net_debug) diag_printf("%s: FEC reset completed\n", __FUNCTION__);
670                         break;
671                 }
672                 hal_delay_us(FEC_MII_TICK);
673         }
674
675         if (value & PHY_CTRL_RESET) {
676                 diag_printf("%s: FEC PHY reset timed out\n", __FUNCTION__);
677                 return false;
678         }
679
680         unsigned long id;
681         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_1, &value);
682         id = (value & PHY_ID1_MASK) << PHY_ID1_SHIFT;
683         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_2, &value);
684         id |= (value & PHY_ID2_MASK) << PHY_ID2_SHIFT;
685         if( id == 0 || id == 0xffffffff) {
686                 diag_printf("FEC could not identify PHY: ID=%08lx\n", id);
687                 return false;
688         }
689
690         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG,
691                           PHY_CTRL_AUTO_NEG | PHY_CTRL_FULL_DPLX);
692
693         timeout = FEC_COMMON_TIMEOUT;
694         while (timeout-- &&
695                mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value) == 0) {
696                 if (value & PHY_STATUS_LINK_ST) {
697                         if (net_debug) diag_printf("PHY Status: %04x\n", value);
698                         break;
699                 }
700                 hal_delay_us(FEC_MII_TICK);
701         }
702         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, &value);
703         value &= ~(PHY_LED_SEL);
704         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, value);
705
706         return true;
707 }
708
709 static int mxc_fec_discover_phy(mxc_fec_priv_t *fep, unsigned char def_addr)
710 {
711         int ret = 0;
712         unsigned char phy_addr = def_addr;
713         unsigned long id = 0;
714         int i;
715         for (i = 0; i < 32; i++) {
716                 unsigned short mii_reg;
717
718                 ret = mxc_fec_mii_read(fep->hw_reg, phy_addr, MII_REG_PHYIR1, &mii_reg);
719
720                 if (ret != 0) {
721                         break;
722                 }
723                 if (mii_reg != 0xffff && mii_reg != 0) {
724                         /* Got first part of ID, now get remainder.
725                         */
726                         id = mii_reg;
727                         ret = mxc_fec_mii_read(fep->hw_reg, phy_addr, MII_REG_PHYIR2, &mii_reg);
728                         if (ret != 0) {
729                                 break;
730                         }
731                         id = (id << 16) | mii_reg;
732                         if (net_debug) diag_printf("%s: discovered PHY %08lx at addr %x\n",
733                                                    __FUNCTION__, id, phy_addr);
734                         ret = phy_addr;
735                         break;
736                 } else {
737                         phy_addr = (phy_addr + 1) % 32;
738                         ret = mxc_fec_mii_read(fep->hw_reg, phy_addr, MII_REG_PHYIR1, &mii_reg);
739                         if (ret != 0) {
740                                 break;
741                         }
742                 }
743         }
744         if (id == 0) {
745                 /* Disable MII */
746                 fep->hw_reg->mscr = 0;
747                 ret = -1;
748         }
749
750         return ret;
751 }
752 #endif
753
754 /*
755  * generic PHY support functions
756  */
757 void mxc_fec_phy_reset(void)
758 {
759         unsigned short value = 0;
760         unsigned long timeout=FEC_COMMON_TIMEOUT;
761         mxc_fec_priv_t *dev = &mxc_fec_private;
762
763         /*Reset PHY*/
764         if (net_debug) diag_printf("%s\n", __FUNCTION__);
765
766         _eth_phy_write(dev->phy, PHY_CTRL_REG, dev->phy->phy_addr, PHY_CTRL_RESET);
767         while (timeout--) {
768                 if (!_eth_phy_read(dev->phy, PHY_CTRL_REG, dev->phy->phy_addr, &value)) {
769                         return;
770                 }
771
772                 if(!(value & PHY_CTRL_RESET)) {
773                         if (net_debug) diag_printf("%s: FEC reset completed\n", __FUNCTION__);
774                         break;
775                 }
776                 hal_delay_us(FEC_MII_TICK);
777         }
778
779         if (value & PHY_CTRL_RESET) {
780                 diag_printf("%s: FEC PHY reset timed out\n", __FUNCTION__);
781                 return;
782         }
783 }
784
785 void mxc_fec_phy_init(void)
786 {
787         if (net_debug) diag_printf("%s\n", __FUNCTION__);
788 }
789
790 bool mxc_fec_phy_read(int reg, int unit, unsigned short *data)
791 {
792         int ret;
793         if (net_debug) diag_printf("%s\n", __FUNCTION__);
794         ret = mxc_fec_mii_read(mxc_fec_private.hw_reg, unit, reg, data);
795         return ret == 0;
796 }
797
798 void mxc_fec_phy_write(int reg, int unit, unsigned short data)
799 {
800         if (net_debug) diag_printf("%s\n", __FUNCTION__);
801         mxc_fec_mii_write(mxc_fec_private.hw_reg, unit, reg, data);
802 }
803
804 /*! This function initializes the FEC driver. 
805  * It is called by net_init in net module of RedBoot during RedBoot init
806  */
807 static bool 
808 mxc_fec_init(struct cyg_netdevtab_entry *tab)
809 {
810         struct eth_drv_sc * sc = tab ? tab->device_instance : NULL;
811         mxc_fec_priv_t * private;
812         char eth_add_local[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
813 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
814         cyg_bool set_esa;
815         int ok;
816
817         /* Get MAC address */
818         ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
819                                          "fec_esa", &set_esa, CONFIG_BOOL);
820         if (ok && set_esa) {
821                 CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
822                                             "fec_esa_data", eth_add_local, CONFIG_ESA);
823         }
824 #endif
825         if (net_debug)  diag_printf("%s:\n", __FUNCTION__);
826         if (sc == NULL){
827                 diag_printf("%s: no driver attached\n", __FUNCTION__);
828                 return false;
829         } 
830         
831         private = MXC_FEC_PRIVATE(sc);
832         if (private == NULL) {
833                 private = MXC_FEC_PRIVATE(sc) = &mxc_fec_private;
834         }
835
836         private->hw_reg = (void *)SOC_FEC_BASE;
837         private->tx_busy = 0;
838         private->status = 0;
839
840         mxc_fec_bd_init(private);
841
842         mxc_fec_chip_init(private);
843 #ifdef CYGPKG_DEVS_ETH_PHY
844         if (!_eth_phy_init(private->phy)) {
845                 diag_printf("%s: Failed to initialize PHY\n", __FUNCTION__);
846                 return false;
847         }
848         _eth_phy_state(private->phy);
849 #else
850         ok = mxc_fec_discover_phy(private, PHY_PORT_ADDR);
851         if (ok < 0) {
852                 diag_printf("%s: no PHY found\n", __FUNCTION__);
853                 return false;
854         }
855         private->phy_addr = ok;
856         mxc_fec_phy_init(private);
857 #endif  
858         /*TODO:: initialize System Resource : irq, timer */
859
860         sc->funs->eth_drv->init(sc, eth_add_local);
861         mxc_fec_phy_status(private, _eth_phy_state(private->phy), true);
862     
863         return true;
864 }
865
866 #ifndef CYGPKG_DEVS_ETH_PHY
867 /*!
868  * Global variable which defines the FEC driver, 
869  */
870 ETH_DRV_SC(mxc_fec_sc,
871            &mxc_fec_private, // Driver specific data
872            mxc_fec_name,
873            mxc_fec_start,
874            mxc_fec_stop,
875            mxc_fec_control,
876            mxc_fec_can_send,
877            mxc_fec_send,
878            mxc_fec_recv,
879            mxc_fec_deliver,     // "pseudoDSR" called from fast net thread
880            mxc_fec_poll,        // poll function, encapsulates ISR and DSR
881            mxc_fec_int_vector);
882
883 /*!
884  * Global variable which defines the FEC device
885  */
886 NETDEVTAB_ENTRY(mxc_fec_netdev,
887                 mxc_fec_name,
888                 mxc_fec_init,
889                 &mxc_fec_sc);
890 #endif