]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/eth/fec/v2_0/src/if_fec.c
unified MX27, MX25, MX37 trees
[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 static bool mxc_fec_init(struct cyg_netdevtab_entry *tab);
95
96 #include <cyg/io/fec.h>
97 #define __WANT_DEVS
98 #include CYGDAT_DEVS_ETH_FEC_INL
99 #undef __WANT_DEVS
100
101 #include <redboot.h>
102
103 #include <cyg/hal/hal_mm.h>
104 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
105 #include <flash_config.h>
106 #endif
107
108
109 #define MII_REG_CR          0  /* Control Register                         */
110 #define MII_REG_SR          1  /* Status Register                          */
111 #define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
112 #define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
113
114 static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool show);
115
116 #ifndef CYGPKG_DEVS_ETH_PHY
117 /*!
118  * Global variable which contains the name of FEC driver and device.
119  */
120 static char  mxc_fec_name[] = "mxc_fec";
121
122 /*!
123  * Global variable which defines the private structure of FEC device.
124  */
125 static mxc_fec_priv_t  mxc_fec_private;
126 #endif
127
128 /*!
129  * Global variable which defines the buffer descriptors for receive frames
130  *      comment:: it must aligned by 128-bits.
131  */
132 static mxc_fec_bd_t mxc_fec_rx_bd[FEC_BD_RX_NUM] __attribute__ ((aligned(32)));
133
134 /*!
135  * Global variable which defines the buffer descriptors for transmit frames
136  *      comment:: it must aligned by 128-bits.
137  */
138 static mxc_fec_bd_t mxc_fec_tx_bd[FEC_BD_TX_NUM] __attribute__ ((aligned(32)));
139
140 /*!
141  * Global variable which contains the frame buffers
142  */
143 static unsigned char mxc_fec_rx_buf[FEC_BD_RX_NUM][2048] __attribute__ ((aligned(32)));
144
145 /*!
146  * Global variable which contains the frame buffers
147  */
148 static unsigned char mxc_fec_tx_buf[FEC_BD_TX_NUM][2048] __attribute__ ((aligned(32)));
149
150 #if 0
151 static void dump_packet (unsigned char *pkt, size_t len)
152 {
153         int i;
154
155         diag_printf("Packet dump: %u byte", len);
156         for (i = 0; i < len; i++) {
157                 if (i % 16 == 0) {
158                         diag_printf("\n%04x:", i);
159                 } else {
160                         if (i % 4 == 0) {
161                                 diag_printf(" ");
162                         }
163                         if (i % 8 == 0) {
164                                 diag_printf(" ");
165                         }
166                 }
167                 diag_printf(" %02x", pkt[i]);
168         }
169         if (i % 16)
170                 diag_printf("\n");
171 }
172 #endif
173
174 #define mxc_fec_reg_read(hw_reg,reg) _mxc_fec_reg_read(&(hw_reg)->reg, #reg)
175 static inline unsigned long _mxc_fec_reg_read(volatile unsigned long *addr,
176                                                                                           const char *reg)
177 {
178         unsigned long val = readl(addr);
179
180         if (net_debug) diag_printf("Read %08lx from FEC reg %s[%p]\n",
181                                    val, reg, addr);
182         return val;
183 }
184
185 #define mxc_fec_reg_write(hw_reg,reg,val) _mxc_fec_reg_write(&(hw_reg)->reg, val, #reg)
186 static inline void _mxc_fec_reg_write(volatile unsigned long *addr,
187                                                                           unsigned long val, const char *reg)
188 {
189         if (net_debug) diag_printf("Writing %08lx to FEC reg %s[%p]\n",
190                                    val, reg, addr);
191         writel(val, addr);
192 }
193
194 #define mxc_fec_reg_read16(hw_reg,reg) _mxc_fec_reg_read16(&(hw_reg)->reg, #reg)
195 static inline unsigned short _mxc_fec_reg_read16(volatile unsigned short *addr,
196                                                                                                 const char *reg)
197 {
198         unsigned short val = readw(addr);
199
200         if (net_debug) diag_printf("Read %04x from FEC reg %s[%p]\n",
201                                    val, reg, addr);
202         return val;
203 }
204
205 #define mxc_fec_reg_write16(hw_reg,reg,val) _mxc_fec_reg_write16(&(hw_reg)->reg, val, #reg)
206 static inline void _mxc_fec_reg_write16(volatile unsigned short *addr,
207                                                                                 unsigned short val, const char *reg)
208 {
209         if (net_debug) diag_printf("Writing %04x to FEC reg %s[%p]\n",
210                                    val, reg, addr);
211         writew(val, addr);
212 }
213
214 /*!
215  * This function gets the value of PHY registers via MII interface
216  */
217 static int
218 mxc_fec_mii_read(volatile mxc_fec_reg_t *hw_reg, unsigned char phy_addr, unsigned char reg_addr,
219                                  unsigned short int *value)
220 {
221         unsigned long waiting = FEC_MII_TIMEOUT;
222
223         if (net_debug) diag_printf("%s: Trying to read phy[%02x] reg %04x\n",
224                                    __FUNCTION__, phy_addr, reg_addr);
225         if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
226                 if (net_debug) diag_printf("%s: Clearing EIR_EVENT_MII\n", __FUNCTION__);
227                 mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
228         }
229         if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
230         mxc_fec_reg_write(hw_reg, mmfr, FEC_MII_READ(phy_addr, reg_addr));/* Write CMD */
231         while (1) {
232                 if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
233                         if (net_debug) diag_printf("%s: Got EIR_EVENT_MII: EIR=%08lx\n",
234                                                    __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
235                         mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
236                         break;
237                 }
238                 if (--waiting == 0) {
239                         diag_printf("%s: Read from PHY at addr %d reg 0x%02x timed out: EIR=%08lx\n",
240                                     __FUNCTION__, phy_addr, reg_addr,
241                                     mxc_fec_reg_read(hw_reg, eir));
242                         return -1;
243                 }
244                 hal_delay_us(FEC_MII_TICK);
245         }
246         *value = FEC_MII_GET_DATA(mxc_fec_reg_read(hw_reg, mmfr));
247         if (net_debug) diag_printf("%s: Read %04x from phy[%02x] reg %04x\n", __FUNCTION__,
248                                    *value, phy_addr, reg_addr);
249         return 0;
250 }
251
252 /*!
253  * This function set the value of  PHY registers by MII interface
254  */
255 static int
256 mxc_fec_mii_write(volatile mxc_fec_reg_t *hw_reg, unsigned char phy_addr, unsigned char reg_addr,
257                   unsigned short int value)
258 {
259         unsigned long waiting = FEC_MII_TIMEOUT;
260
261         if (net_debug) diag_printf("%s: Trying to write %04x to phy[%02x] reg %04x\n", __FUNCTION__,
262                                    value, phy_addr, reg_addr);
263         if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
264                 if (net_debug) diag_printf("%s: Clearing EIR_EVENT_MII\n", __FUNCTION__);
265                 mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
266         }
267         if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
268         mxc_fec_reg_write(hw_reg, mmfr,  FEC_MII_WRITE(phy_addr, reg_addr, value));/* Write CMD */
269         if (net_debug) diag_printf("%s: Wrote cmd %08x to MMFR\n", __FUNCTION__,
270                                    FEC_MII_WRITE(phy_addr, reg_addr, value));
271         while (1) {
272                 if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
273                         if (net_debug) diag_printf("%s: Got EIR_EVENT_MII: EIR=%08lx\n",
274                                                    __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
275                         mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
276                         break;
277                 }
278                 if (--waiting == 0) {
279                         diag_printf("%s: Write to PHY at addr %d reg 0x%02x timed out: EIR=%08lx\n",
280                                     __FUNCTION__, phy_addr, reg_addr,
281                                     mxc_fec_reg_read(hw_reg, eir));
282                         return -1;
283                 }
284                 hal_delay_us(FEC_MII_TICK);
285         }
286         if (net_debug) diag_printf("%s: Write to phy register succeeded\n", __FUNCTION__);
287         return 0;
288 }
289
290 static void
291 mxc_fec_set_mac_address(volatile mxc_fec_reg_t *hw_reg, unsigned char *enaddr)
292 {
293         unsigned long value;
294
295         value = enaddr[0];
296         value = (value << 8) + enaddr[1];
297         value = (value << 8) + enaddr[2];
298         value = (value << 8) + enaddr[3];
299         mxc_fec_reg_write(hw_reg, palr, value);
300
301         value = enaddr[4];
302         value = (value << 8) + enaddr[5];
303         mxc_fec_reg_write(hw_reg, paur, value << 16);
304 }
305
306 /*!
307  * This function enables the FEC for reception of packets
308  */
309 static void
310 mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
311 {
312         mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
313         volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
314
315         if (!(priv && hw_reg)) {
316                 diag_printf("BUG[start]: FEC driver not initialized\n");
317                 return;
318         }
319         if (enaddr == NULL) {
320                 diag_printf("BUG[start]: no MAC address supplied\n");
321                 return;
322         }
323         mxc_fec_set_mac_address(hw_reg, enaddr);
324
325         priv->tx_busy = 0;
326         mxc_fec_reg_write(hw_reg, rdar, mxc_fec_reg_read(hw_reg, rdar) | FEC_RX_TX_ACTIVE);
327         mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_ETHER_EN);
328 #ifdef CYGPKG_HAL_ARM_MX25
329         /*
330          * setup the MII gasket for RMII mode
331          */
332
333         /* disable the gasket */
334         mxc_fec_reg_write16(hw_reg, miigsk_enr, 0);
335
336         /* wait for the gasket to be disabled */
337         while (mxc_fec_reg_read16(hw_reg, miigsk_enr) & MIIGSK_ENR_READY)
338                 hal_delay_us(FEC_COMMON_TICK);
339
340         /* configure gasket for RMII, 50 MHz, no loopback, and no echo */
341         mxc_fec_reg_write16(hw_reg, miigsk_cfgr, MIIGSK_CFGR_IF_MODE_RMII);
342
343         /* re-enable the gasket */
344         mxc_fec_reg_write16(hw_reg, miigsk_enr, MIIGSK_ENR_EN);
345
346         /* wait until MII gasket is ready */
347         int max_loops = 10;
348         while ((mxc_fec_reg_read16(hw_reg, miigsk_enr) & MIIGSK_ENR_READY) == 0) {
349                 if (--max_loops <= 0) {
350                         diag_printf("WAIT for MII Gasket ready timed out\n");
351                         break;
352                 }
353         }
354 #endif
355 }
356
357 /*!
358  * This function pauses the FEC controller.
359  */
360 static void
361 mxc_fec_stop(struct eth_drv_sc *sc)
362 {
363         mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
364         volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
365
366         if (!(priv && hw_reg)) {
367                 diag_printf("BUG[stop]: FEC driver not initialized\n");
368                 return;
369         }
370         mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) & ~FEC_ETHER_EN);
371 }
372
373 static int
374 mxc_fec_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length)
375 {
376         /*TODO:: Add support */
377         diag_printf("mxc_fec_control: key=0x%08lx, data=%p, data_len=0x%08x\n",
378                     key, data, data_length);
379         return 0;
380 }
381
382 /*!
383  * This function checks the status of FEC control.
384  */
385 static int
386 mxc_fec_can_send(struct eth_drv_sc *sc)
387 {
388         mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
389         volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
390
391         if (!(priv && hw_reg)) {
392                 diag_printf("BUG[can_send]: FEC driver not initialized\n");
393                 return 0;
394         }
395         if (priv->tx_busy) {
396                 diag_printf("WARNING[can_send]: MXC_FEC is busy for transmission\n");
397                 return 0;
398         }
399
400         if (!(mxc_fec_reg_read(hw_reg, ecr) & FEC_ETHER_EN)) {
401                 diag_printf("WARNING[can_send]: MXC_FEC is not enabled\n");
402                 return 0;
403         }
404
405         if (mxc_fec_reg_read(hw_reg, tcr) & FEC_TCR_RFC_PAUSE) {
406                 diag_printf("WARNING[can_send]: MXC_FEC is paused\n");
407                 return 0;
408         }
409
410         if (!(priv->status & FEC_STATUS_LINK_ON)) {
411                 /* Reading the PHY status for every packet to be sent is
412                  * a real performance killer.
413                  * Thus, only read the PHY status when the link is down to
414                  * detect a possible new connection
415                  */
416 #ifdef CYGPKG_DEVS_ETH_PHY
417                 unsigned short value;
418                 value = _eth_phy_state(priv->phy);
419 #else
420                 unsigned short value;
421                 mxc_fec_mii_read(hw_reg, priv->phy_addr, 1, &value);
422 #endif
423                 if (value & PHY_STATUS_LINK_ST) {
424                         if (!(priv->status & FEC_STATUS_LINK_ON)) {
425                                 mxc_fec_phy_status(priv, value, true);
426                         }
427                 } else {
428                         if (priv->status & FEC_STATUS_LINK_ON) {
429                                 mxc_fec_phy_status(priv, value, true);
430                         }
431                 }
432         }
433
434         return priv->status & FEC_STATUS_LINK_ON;
435 }
436
437 /*!
438  * This function transmits a frame.
439  */
440 static void
441 mxc_fec_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total,
442                          unsigned long key)
443 {
444         mxc_fec_priv_t *dev = sc ? sc->driver_private : NULL;
445         volatile mxc_fec_reg_t *hw_reg = dev ? dev->hw_reg : NULL;
446         mxc_fec_bd_t *p;
447         int i, off;
448
449         if (dev == NULL || hw_reg == NULL) {
450                 diag_printf("BUG[TX]: FEC driver not initialized\n");
451                 return;
452         }
453         if (total > (FEC_FRAME_LEN - 4)) total = FEC_FRAME_LEN - 4;
454         if (sg_list == NULL || total <= 14) {
455                 if (sc->funs->eth_drv && sc->funs->eth_drv->tx_done) {
456                         sc->funs->eth_drv->tx_done(sc, key, -1);
457                 }
458                 return;
459         }
460
461         for (i = 0, off = 0, p = dev->tx_cur; i < sg_len; i++) {
462                 unsigned long vaddr;
463
464                 if (p->status & BD_TX_ST_RDY) {
465                         diag_printf("BUG[TX]: trying to resend already finished buffer\n");
466                         break;
467                 }
468                 if (sg_list[i].buf == 0) {
469                         diag_printf("WARNING[TX]: sg_list->buf is NULL\n");
470                         break;
471                 }
472                 vaddr = hal_ioremap_nocache((unsigned long)p->data) + off;
473                 memcpy((void *)vaddr, (void *)sg_list[i].buf, sg_list[i].len);
474                 off += sg_list[i].len;
475         }
476         if (off < 14) {
477                 diag_printf("WARNING[TX]: packet size %d too small\n", off);
478                 return;
479         }
480         p->length = off;
481         //p->status &= ~(BD_TX_ST_LAST | BD_TX_ST_RDY | BD_TX_ST_TC | BD_TX_ST_ABC);
482         p->status &= ~BD_TX_ST_ABC;
483         p->status |= BD_TX_ST_LAST | BD_TX_ST_RDY | BD_TX_ST_TC;
484         if (p->status & BD_TX_ST_WRAP) {
485                 p = dev->tx_bd;
486         } else {
487                 p++;
488         }
489         dev->tx_cur = p;
490         dev->tx_busy = 1;
491         dev->tx_key = key;
492         mxc_fec_reg_write(hw_reg, tdar, FEC_RX_TX_ACTIVE);
493 }
494
495 /*!
496  * This function receives ready Frame in DB.
497  */
498 static void
499 mxc_fec_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
500 {
501         mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
502         mxc_fec_bd_t *p;
503         unsigned long vaddr;
504
505         if (sg_list == NULL || priv == NULL || sg_len <= 0) {
506                 diag_printf("BUG[RX]: FEC driver not initialized\n");
507                 return;
508         }
509
510         /*TODO: I think if buf pointer is NULL, this function
511          * should not be called
512          */
513         if (sg_list->buf == 0) {
514                 diag_printf("WARING[RX]: the sg_list is empty\n");
515                 return;
516         }
517         p = priv->rx_cur;
518
519         if (p->status & BD_RX_ST_EMPTY) {
520                 diag_printf("BUG[RX]: empty buffer received; status=%04x\n", p->status);
521                 return;
522         }
523
524         if (!(p->status & BD_RX_ST_LAST)) {
525                 diag_printf("BUG[RX]: status=%0xx\n", p->status);
526                 return;
527         }
528         vaddr = hal_ioremap_nocache((unsigned long)p->data);
529         /*TODO::D_CACHE invalid this data buffer*/
530         memcpy((void *)sg_list->buf, (void *)vaddr, p->length - 4);
531 }
532
533 static void
534 mxc_fec_deliver(struct eth_drv_sc *sc)
535 {
536         /*TODO::When redboot support thread ,
537          *      the polling function will be called at here
538          */
539         return;
540 }
541
542 /* This funtion just called by polling funtion */
543 static void
544 mxc_fec_check_rx_bd(struct eth_drv_sc *sc)
545 {
546         mxc_fec_priv_t *priv = sc->driver_private;
547         mxc_fec_bd_t *p;
548         volatile mxc_fec_reg_t *hw_reg = priv->hw_reg;
549         int i;
550
551         for (i = 0, p = priv->rx_cur; i < FEC_RX_FRAMES; i++) {
552                 /*
553                  * TODO::D-CACHE invalidate this BD.
554                  * In WRITE_BACK mode: this may destroy the next BD
555                  * when the CACHE_LINE is written back.
556                  */
557                 if (p->status & BD_RX_ST_EMPTY) {
558                         break;
559                 }
560                 if (!(p->status & BD_RX_ST_LAST)) {
561                         diag_printf("BUG[RX]: status=%04x, length=%x\n", p->status, p->length);
562                         goto skip_next;
563                 }
564
565                 if (p->status & BD_RX_ST_ERRS) {
566                         diag_printf("RX error: status=%08x errors=%08x\n", p->status,
567                                                 p->status & BD_RX_ST_ERRS);
568                 } else if (p->length > FEC_FRAME_LEN) {
569                         diag_printf("RX error: packet size 0x%08x larger than max frame length: 0x%08x\n",
570                                                 p->length, FEC_FRAME_LEN);
571                 } else {
572                         sc->funs->eth_drv->recv(sc, p->length - 4);
573                 }
574 skip_next:
575                 p->status = (p->status & BD_RX_ST_WRAP) | BD_RX_ST_EMPTY;
576
577                 if (p->status & BD_RX_ST_WRAP) {
578                         p = priv->rx_bd;
579                 } else {
580                         p++;
581                 }
582                 priv->rx_cur = p;
583                 mxc_fec_reg_write(hw_reg, rdar, mxc_fec_reg_read(hw_reg, rdar) | FEC_RX_TX_ACTIVE);
584         }
585 }
586
587 /*!
588  * This function checks the event of FEC controller
589  */
590 static void
591 mxc_fec_poll(struct eth_drv_sc *sc)
592 {
593         mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
594         volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
595         unsigned long value;
596         int dbg = net_debug;
597
598         if (priv == NULL || hw_reg == NULL) {
599                 diag_printf("BUG[POLL]: FEC driver not initialized\n");
600                 return;
601         }
602 #if 1
603         net_debug = 0;
604 #endif
605         value = mxc_fec_reg_read(hw_reg, eir);
606         mxc_fec_reg_write(hw_reg, eir, value & ~FEC_EVENT_MII);
607 #if 1
608         net_debug = dbg;
609 #endif
610         if (value & FEC_EVENT_TX_ERR) {
611                 diag_printf("WARNING[POLL]: Transmit error\n");
612                 sc->funs->eth_drv->tx_done(sc, priv->tx_key, -1);
613                 priv->tx_busy = 0;
614         } else {
615                 if (value & FEC_EVENT_TX) {
616                         sc->funs->eth_drv->tx_done(sc, priv->tx_key, 0);
617                         priv->tx_busy = 0;
618                 }
619         }
620
621         if (value & FEC_EVENT_RX) {
622                 mxc_fec_check_rx_bd(sc);
623         }
624
625         if (value & FEC_EVENT_HBERR) {
626                 diag_printf("WARNGING[POLL]: Hearbeat error!\n");
627         }
628
629         if (value & FEC_EVENT_EBERR) {
630                 diag_printf("WARNING[POLL]: Ethernet Bus Error!\n");
631         }
632 }
633
634 static int
635 mxc_fec_int_vector(struct eth_drv_sc *sc)
636 {
637         /*TODO::
638          *      get FEC interrupt number
639          */
640         return -1;
641 }
642
643 /*!
644  * The function initializes the description buffer for receiving or transmitting
645  */
646 static void
647 mxc_fec_bd_init(mxc_fec_priv_t *dev)
648 {
649         int i;
650         mxc_fec_bd_t *p;
651
652         p = dev->rx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_rx_bd));
653         for (i = 0; i < FEC_BD_RX_NUM; i++, p++) {
654                 p->status = BD_RX_ST_EMPTY;
655                 p->length = 0;
656                 p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_rx_buf[i]);
657         }
658
659         dev->rx_bd[i - 1].status |= BD_RX_ST_WRAP;
660         dev->rx_cur = dev->rx_bd;
661
662         p = dev->tx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_tx_bd));
663         for (i = 0; i < FEC_BD_TX_NUM; i++, p++) {
664                 p->status = 0;
665                 p->length = 0;
666                 p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_tx_buf[i]);
667         }
668
669         dev->tx_bd[i - 1].status |= BD_TX_ST_WRAP;
670         dev->tx_cur = dev->tx_bd;
671
672         /*TODO:: add the sync function for items*/
673 }
674
675 /*!
676  *This function initializes FEC controller.
677  */
678 static void
679 mxc_fec_chip_init(mxc_fec_priv_t *dev)
680 {
681         volatile mxc_fec_reg_t *hw_reg = dev->hw_reg;
682         unsigned long ipg_clk;
683
684         mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_RESET);
685         while (mxc_fec_reg_read(hw_reg, ecr) & FEC_RESET) {
686                 hal_delay_us(FEC_COMMON_TICK);
687         }
688
689         mxc_fec_reg_write(hw_reg, eimr, 0);
690         mxc_fec_reg_write(hw_reg, eir, ~0);
691
692         mxc_fec_reg_write(hw_reg, rcr,
693                                           (mxc_fec_reg_read(hw_reg, rcr) & ~0x3F) |
694                                           FEC_RCR_FCE | FEC_RCR_MII_MODE);
695
696         mxc_fec_reg_write(hw_reg, tcr, mxc_fec_reg_read(hw_reg, tcr) | FEC_TCR_FDEN);
697         mxc_fec_reg_write(hw_reg, mibc, mxc_fec_reg_read(hw_reg, mibc) | FEC_MIB_DISABLE);
698
699         mxc_fec_reg_write(hw_reg, iaur, 0);
700         mxc_fec_reg_write(hw_reg, ialr, 0);
701         mxc_fec_reg_write(hw_reg, gaur, 0);
702         mxc_fec_reg_write(hw_reg, galr, 0);
703
704         ipg_clk = get_main_clock(IPG_CLK);
705
706         mxc_fec_reg_write(hw_reg, mscr,
707                                           (mxc_fec_reg_read(hw_reg, mscr) & ~0x7e) |
708                                           (((ipg_clk + 499999) / 2500000 / 2) << 1));
709         if (net_debug) diag_printf("mscr set to %08lx for ipg_clk %ld\n",
710                                    mxc_fec_reg_read(hw_reg, mscr), ipg_clk);
711
712         mxc_fec_reg_write(hw_reg, emrbr, 2048 - 16);
713         mxc_fec_reg_write(hw_reg, erdsr, hal_virt_to_phy((unsigned long)dev->rx_bd));
714         mxc_fec_reg_write(hw_reg, etdsr, hal_virt_to_phy((unsigned long)dev->tx_bd));
715
716         /* must be done before enabling the MII gasket
717          * (otherwise MIIGSK_ENR_READY will never assert)
718          */
719         mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_ETHER_EN);
720 }
721
722 static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool show)
723 {
724 #ifdef CYGPKG_DEVS_ETH_PHY
725         if (value & ETH_PHY_STAT_LINK) {
726                 dev->status |= FEC_STATUS_LINK_ON;
727                 if (value & ETH_PHY_STAT_FDX) {
728                         dev->status |= FEC_STATUS_FULL_DPLX;
729                 } else {
730                         dev->status &= ~FEC_STATUS_FULL_DPLX;
731                 }
732                 if (value & ETH_PHY_STAT_100MB) {
733                         dev->status |= FEC_STATUS_100M;
734                 } else {
735                         dev->status &= ~FEC_STATUS_100M;
736                 }
737         } else {
738                 dev->status &= ~FEC_STATUS_LINK_ON;
739         }
740 #else
741         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value);
742         if (value & PHY_STATUS_LINK_ST) {
743                 dev->status |= FEC_STATUS_LINK_ON;
744         } else {
745                 dev->status &= ~FEC_STATUS_LINK_ON;
746         }
747
748         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_DIAG_REG, &value);
749         if (value & PHY_DIAG_DPLX) {
750                 dev->status |= FEC_STATUS_FULL_DPLX;
751         } else {
752                 dev->status &= ~FEC_STATUS_FULL_DPLX;
753         }
754         if (value & PHY_DIAG_RATE) {
755                 dev->status |= FEC_STATUS_100M;
756         } else {
757                 dev->status &= ~FEC_STATUS_100M;
758         }
759 #endif
760         if (!show) {
761                 return;
762         }
763         if (dev->status & FEC_STATUS_LINK_ON) {
764                 diag_printf("FEC: [ %s ] [ %s ]:\n",
765                             (dev->status & FEC_STATUS_FULL_DPLX) ? "FULL_DUPLEX" : "HALF_DUPLEX",
766                             (dev->status & FEC_STATUS_100M) ? "100 Mbps" : "10 Mbps");
767         } else {
768                 diag_printf("FEC: no cable\n");
769         }
770 }
771
772 #ifndef CYGPKG_DEVS_ETH_PHY
773 /*!
774  * This function initializes the PHY
775  */
776 static bool
777 mxc_fec_phy_init(mxc_fec_priv_t *dev)
778 {
779 #if 1
780         unsigned short value = 0;
781         unsigned long timeout = FEC_COMMON_TIMEOUT;
782
783         /*Reset PHY*/
784         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, PHY_CTRL_RESET);
785         while (timeout--) {
786                 if (mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, &value)) {
787                         return false;
788                 }
789
790                 if (!(value & PHY_CTRL_RESET)) {
791                         if (net_debug) diag_printf("%s: FEC reset completed\n", __FUNCTION__);
792                         break;
793                 }
794                 hal_delay_us(FEC_MII_TICK);
795         }
796
797         if (value & PHY_CTRL_RESET) {
798                 diag_printf("%s: FEC PHY reset timed out\n", __FUNCTION__);
799                 return false;
800         }
801
802         unsigned long id;
803         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_1, &value);
804         id = (value & PHY_ID1_MASK) << PHY_ID1_SHIFT;
805         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_2, &value);
806         id |= (value & PHY_ID2_MASK) << PHY_ID2_SHIFT;
807         if (id == 0 || id == 0xffffffff) {
808                 diag_printf("FEC could not identify PHY: ID=%08lx\n", id);
809                 return false;
810         }
811
812         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG,
813                           PHY_CTRL_AUTO_NEG | PHY_CTRL_FULL_DPLX);
814
815         timeout = FEC_COMMON_TIMEOUT;
816         while (timeout-- &&
817                mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value) == 0) {
818                 if (value & PHY_STATUS_LINK_ST) {
819                         if (net_debug) diag_printf("PHY Status: %04x\n", value);
820                         break;
821                 }
822                 hal_delay_us(FEC_MII_TICK);
823         }
824         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, &value);
825         value &= ~PHY_LED_SEL;
826         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, value);
827 #else
828         unsigned long value = 0;
829         unsigned long id = 0, timeout = 50;
830
831         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_1, &value);
832         id = (value & PHY_ID1_MASK) << PHY_ID1_SHIFT;
833         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_2, &value);
834         id |= (value & PHY_ID2_MASK) << PHY_ID2_SHIFT;
835
836         switch (id) {
837         case 0x00540088:
838                 break;
839         case 0x00007C0C:
840                 break;
841         default:
842                 diag_printf("[Warning] FEC not connect right PHY: ID=%lx\n", id);
843         }
844
845         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, PHY_CTRL_AUTO_NEG|PHY_CTRL_FULL_DPLX);
846
847 #ifdef CYGPKG_HAL_ARM_MX27ADS
848         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, &value);
849         value &= ~PHY_LED_SEL;
850         mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, value);
851 #endif
852
853 #if defined(CYGPKG_HAL_ARM_MX51) || defined (CYGPKG_HAL_ARM_MX25_3STACK) || defined (CYGPKG_HAL_ARM_MX35_3STACK) || defined (CYGPKG_HAL_ARM_MX27_3STACK)
854         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_AUTO_NEG_EXP_REG, &value);
855         /* Wait for packet to arrive */
856         while (((value & PHY_AUTO_NEG_NEW_PAGE) == 0) && (timeout != 0)) {
857                 hal_delay_us(100);
858                 mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_AUTO_NEG_EXP_REG, &value);
859                 timeout--;
860         }
861         /* Check if link is capable of auto-negotiation */
862         if ((value & PHY_AUTO_NEG_CAP) == 1) {
863                 mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_INT_SRC_REG, &value);
864                 timeout = 50;
865                 /* Wait for auto-negotiation to complete */
866                 while (((value & PHY_INT_AUTO_NEG) == 0) && (timeout != 0)) {
867                         hal_delay_us(100);
868                         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_INT_SRC_REG, &value);
869                         timeout--;
870                 }
871         }
872 #endif
873         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value);
874         if (value & PHY_STATUS_LINK_ST) {
875                 dev->status |= FEC_STATUS_LINK_ON;
876         } else {
877                 dev->status &= ~FEC_STATUS_LINK_ON;
878         }
879
880 #ifdef CYGPKG_HAL_ARM_MX27ADS
881         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_DIAG_REG, &value);
882         if (value & PHY_DIAG_DPLX) {
883                 dev->status |= FEC_STATUS_FULL_DPLX;
884         } else {
885                 dev->status &= ~FEC_STATUS_FULL_DPLX;
886         }
887         if (value & PHY_DIAG_DPLX) {
888                 dev->status |= FEC_STATUS_100M;
889         } else {
890                 dev->status &= ~FEC_STATUS_100M;
891         }
892 #endif
893
894 #if defined(CYGPKG_HAL_ARM_MX51) || defined (CYGPKG_HAL_ARM_MX25_3STACK) || defined (CYGPKG_HAL_ARM_MX35_3STACK)
895         mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_AUTO_NEG_REG, &value);
896         if (value & PHY_AUTO_10BASET) {
897                 dev->status &= ~FEC_STATUS_100M;
898                 if (value & PHY_AUTO_10BASET_DPLX) {
899                         dev->status |= FEC_STATUS_FULL_DPLX;
900                 } else {
901                         dev->status &= ~FEC_STATUS_FULL_DPLX;
902                 }
903         }
904
905         if (value & PHY_AUTO_100BASET) {
906                 dev->status |= FEC_STATUS_100M;
907                 if (value & PHY_AUTO_100BASET_DPLX) {
908                         dev->status |= FEC_STATUS_FULL_DPLX;
909                 } else {
910                         dev->status &= ~FEC_STATUS_FULL_DPLX;
911                 }
912         }
913 #endif
914         diag_printf("FEC: [ %s ] [ %s ] [ %s ]:\n",
915                 (dev->status&FEC_STATUS_FULL_DPLX)?"FULL_DUPLEX":"HALF_DUPLEX",
916                 (dev->status&FEC_STATUS_LINK_ON)?"connected":"disconnected",
917                 (dev->status&FEC_STATUS_100M)?"100M bps":"10M bps");
918 #endif
919         return true;
920 }
921
922 static int mxc_fec_discover_phy(mxc_fec_priv_t *fep, unsigned char def_addr)
923 {
924         int ret = 0;
925         unsigned char phy_addr = def_addr;
926         unsigned long id = 0;
927         int i;
928         for (i = 0; i < 32; i++) {
929                 unsigned short mii_reg;
930
931                 ret = mxc_fec_mii_read(fep->hw_reg, phy_addr, MII_REG_PHYIR1, &mii_reg);
932
933                 if (ret != 0) {
934                         break;
935                 }
936                 if (mii_reg != 0xffff && mii_reg != 0) {
937                         /* Got first part of ID, now get remainder.
938                         */
939                         id = mii_reg;
940                         ret = mxc_fec_mii_read(fep->hw_reg, phy_addr, MII_REG_PHYIR2, &mii_reg);
941                         if (ret != 0) {
942                                 break;
943                         }
944                         id = (id << 16) | mii_reg;
945                         if (net_debug) diag_printf("%s: discovered PHY %08lx at addr %x\n",
946                                                    __FUNCTION__, id, phy_addr);
947                         ret = phy_addr;
948                         break;
949                 } else {
950                         phy_addr = (phy_addr + 1) % 32;
951                         ret = mxc_fec_mii_read(fep->hw_reg, phy_addr, MII_REG_PHYIR1, &mii_reg);
952                         if (ret != 0) {
953                                 break;
954                         }
955                 }
956         }
957         if (id == 0) {
958                 /* Disable MII */
959                 fep->mxc_fec_reg_write(hw_reg, mscr, 0);
960                 ret = -1;
961         }
962
963         return ret;
964 }
965 #endif
966
967 /*
968  * generic PHY support functions
969  */
970 void mxc_fec_phy_reset(void)
971 {
972         unsigned short value = 0;
973         unsigned long timeout=FEC_COMMON_TIMEOUT;
974         mxc_fec_priv_t *dev = &mxc_fec_private;
975
976         /* Reset PHY */
977         if (net_debug) diag_printf("%s\n", __FUNCTION__);
978
979         _eth_phy_write(dev->phy, PHY_CTRL_REG, dev->phy->phy_addr, PHY_CTRL_RESET);
980         while (timeout--) {
981                 if (!_eth_phy_read(dev->phy, PHY_CTRL_REG, dev->phy->phy_addr, &value)) {
982                         return;
983                 }
984
985                 if (!(value & PHY_CTRL_RESET)) {
986                         if (net_debug) diag_printf("%s: FEC reset completed\n", __FUNCTION__);
987                         break;
988                 }
989                 hal_delay_us(FEC_MII_TICK);
990         }
991
992         if (value & PHY_CTRL_RESET) {
993                 diag_printf("%s: FEC PHY reset timed out\n", __FUNCTION__);
994                 return;
995         }
996 }
997
998 void mxc_fec_phy_init(void)
999 {
1000         if (net_debug) diag_printf("%s\n", __FUNCTION__);
1001 }
1002
1003 bool mxc_fec_phy_read(int reg, int unit, unsigned short *data)
1004 {
1005         int ret;
1006         if (net_debug) diag_printf("%s\n", __FUNCTION__);
1007         ret = mxc_fec_mii_read(mxc_fec_private.hw_reg, unit, reg, data);
1008         return ret == 0;
1009 }
1010
1011 void mxc_fec_phy_write(int reg, int unit, unsigned short data)
1012 {
1013         if (net_debug) diag_printf("%s\n", __FUNCTION__);
1014         mxc_fec_mii_write(mxc_fec_private.hw_reg, unit, reg, data);
1015 }
1016
1017 /*! This function initializes the FEC driver.
1018  * It is called by net_init in net module of RedBoot during RedBoot init
1019  */
1020 static bool
1021 mxc_fec_init(struct cyg_netdevtab_entry *tab)
1022 {
1023         struct eth_drv_sc *sc = tab ? tab->device_instance : NULL;
1024         mxc_fec_priv_t *private;
1025         unsigned char eth_add_local[ETHER_ADDR_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1026         int ok = 0;
1027 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
1028         cyg_bool set_esa;
1029 #endif
1030
1031         if (net_debug)  diag_printf("%s:\n", __FUNCTION__);
1032         if (sc == NULL) {
1033                 diag_printf("%s: no driver attached\n", __FUNCTION__);
1034                 return false;
1035         }
1036
1037         private = MXC_FEC_PRIVATE(sc);
1038         if (private == NULL) {
1039                 private = &mxc_fec_private;
1040         }
1041         if (private->provide_esa) {
1042                 ok = private->provide_esa(eth_add_local);
1043         }
1044 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
1045         if (!ok) {
1046                 /* Get MAC address from fconfig */
1047                 ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
1048                                                                                  "fec_esa", &set_esa, CONFIG_BOOL);
1049                 if (ok && set_esa) {
1050                         CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
1051                                                                                 "fec_esa_data", eth_add_local, CONFIG_ESA);
1052                 }
1053         }
1054 #endif
1055         if (!ok) {
1056                 diag_printf("No ESA provided via fuses or RedBoot config\n");
1057                 return false;
1058         }
1059
1060         private->hw_reg = (void *)SOC_FEC_BASE;
1061         private->tx_busy = 0;
1062         private->status = 0;
1063
1064         mxc_fec_bd_init(private);
1065
1066         mxc_fec_chip_init(private);
1067 #ifdef CYGPKG_DEVS_ETH_PHY
1068         if (!_eth_phy_init(private->phy)) {
1069                 diag_printf("%s: Failed to initialize PHY\n", __FUNCTION__);
1070                 return false;
1071         }
1072         _eth_phy_state(private->phy);
1073 #else
1074         ok = mxc_fec_discover_phy(private, PHY_PORT_ADDR);
1075         if (ok < 0) {
1076                 diag_printf("%s: no PHY found\n", __FUNCTION__);
1077                 return false;
1078         }
1079         private->phy_addr = ok;
1080         mxc_fec_phy_init(private);
1081 #endif
1082         /* TODO:: initialize System Resource : irq, timer */
1083
1084         sc->funs->eth_drv->init(sc, eth_add_local);
1085         mxc_fec_phy_status(private, _eth_phy_state(private->phy), true);
1086
1087         return true;
1088 }
1089
1090 #ifndef CYGPKG_DEVS_ETH_PHY
1091 /*!
1092  * Global variable which defines the FEC driver,
1093  */
1094 ETH_DRV_SC(mxc_fec_sc,
1095            &mxc_fec_private, // Driver specific data
1096            mxc_fec_name,
1097            mxc_fec_start,
1098            mxc_fec_stop,
1099            mxc_fec_control,
1100            mxc_fec_can_send,
1101            mxc_fec_send,
1102            mxc_fec_recv,
1103            mxc_fec_deliver,     // "pseudoDSR" called from fast net thread
1104            mxc_fec_poll,        // poll function, encapsulates ISR and DSR
1105            mxc_fec_int_vector);
1106
1107 /*!
1108  * Global variable which defines the FEC device
1109  */
1110 NETDEVTAB_ENTRY(mxc_fec_netdev,
1111                 mxc_fec_name,
1112                 mxc_fec_init,
1113                 &mxc_fec_sc);
1114
1115 #endif // CYGPKG_DEVS_ETH_PHY