//
// dev/if_fec.c
//
-// Device driver for FEC
+// Device driver for FEC
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
//#####DESCRIPTIONBEGIN####
//
// Author(s): Fred Fan
-// Contributors:
+// Contributors:
// Date: 2006-08-23
-// Purpose:
+// Purpose:
// Description: Driver for FEC ethernet controller
//
-// Note:
+// Note:
//
//####DESCRIPTIONEND####
//
#include <cyg/io/eth_phy.h>
#endif
+static bool mxc_fec_init(struct cyg_netdevtab_entry *tab);
#include <cyg/io/fec.h>
#define __WANT_DEVS
#ifndef CYGPKG_DEVS_ETH_PHY
/*!
- * Global variable which contains the name of FEC driver and device.
+ * Global variable which contains the name of FEC driver and device.
*/
static char mxc_fec_name[] = "mxc_fec";
/*!
* Global variable which defines the private structure of FEC device.
*/
-static mxc_fec_priv_t mxc_fec_private ;
+static mxc_fec_priv_t mxc_fec_private;
#endif
/*!
- *Global variable which defines the buffer descriptions for receiving frame
+ * Global variable which defines the buffer descriptors for receive frames
* comment:: it must aligned by 128-bits.
*/
-static mxc_fec_bd_t mxc_fec_rx_bd[FEC_BD_RX_NUM] __attribute__ ( ( aligned(32) ) ) ;
+static mxc_fec_bd_t mxc_fec_rx_bd[FEC_BD_RX_NUM] __attribute__ ((aligned(32)));
/*!
- *Global variable which defines the buffer descriptions for receiving frame
+ * Global variable which defines the buffer descriptors for transmit frames
* comment:: it must aligned by 128-bits.
*/
-static mxc_fec_bd_t mxc_fec_tx_bd[FEC_BD_TX_NUM] __attribute__ ( ( aligned(32) ) ) ;
+static mxc_fec_bd_t mxc_fec_tx_bd[FEC_BD_TX_NUM] __attribute__ ((aligned(32)));
/*!
- * Global variable which contains the frame buffers ,
+ * Global variable which contains the frame buffers
*/
-static unsigned char mxc_fec_rx_buf[FEC_BD_RX_NUM][2048] __attribute__ ( ( aligned(32) ) ) ;
+static unsigned char mxc_fec_rx_buf[FEC_BD_RX_NUM][2048] __attribute__ ((aligned(32)));
/*!
- * Global variable which contains the frame buffers ,
+ * Global variable which contains the frame buffers
*/
-static unsigned char mxc_fec_tx_buf[FEC_BD_TX_NUM][2048] __attribute__ ( ( aligned(32) ) ) ;
+static unsigned char mxc_fec_tx_buf[FEC_BD_TX_NUM][2048] __attribute__ ((aligned(32)));
+#if 0
+static void dump_packet (unsigned char *pkt, size_t len)
+{
+ int i;
+
+ diag_printf("Packet dump: %u byte", len);
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0) {
+ diag_printf("\n%04x:", i);
+ } else {
+ if (i % 4 == 0) {
+ diag_printf(" ");
+ }
+ if (i % 8 == 0) {
+ diag_printf(" ");
+ }
+ }
+ diag_printf(" %02x", pkt[i]);
+ }
+ if (i % 16)
+ diag_printf("\n");
+}
+#endif
+
+#define mxc_fec_reg_read(hw_reg,reg) _mxc_fec_reg_read(&(hw_reg)->reg, #reg)
+static inline unsigned long _mxc_fec_reg_read(volatile unsigned long *addr,
+ const char *reg)
+{
+ unsigned long val = readl(addr);
+
+ if (net_debug) diag_printf("Read %08lx from FEC reg %s[%p]\n",
+ val, reg, addr);
+ return val;
+}
+
+#define mxc_fec_reg_write(hw_reg,reg,val) _mxc_fec_reg_write(&(hw_reg)->reg, val, #reg)
+static inline void _mxc_fec_reg_write(volatile unsigned long *addr,
+ unsigned long val, const char *reg)
+{
+ if (net_debug) diag_printf("Writing %08lx to FEC reg %s[%p]\n",
+ val, reg, addr);
+ writel(val, addr);
+}
+
+#define mxc_fec_reg_read16(hw_reg,reg) _mxc_fec_reg_read16(&(hw_reg)->reg, #reg)
+static inline unsigned short _mxc_fec_reg_read16(volatile unsigned short *addr,
+ const char *reg)
+{
+ unsigned short val = readw(addr);
+
+ if (net_debug) diag_printf("Read %04x from FEC reg %s[%p]\n",
+ val, reg, addr);
+ return val;
+}
+
+#define mxc_fec_reg_write16(hw_reg,reg,val) _mxc_fec_reg_write16(&(hw_reg)->reg, val, #reg)
+static inline void _mxc_fec_reg_write16(volatile unsigned short *addr,
+ unsigned short val, const char *reg)
+{
+ if (net_debug) diag_printf("Writing %04x to FEC reg %s[%p]\n",
+ val, reg, addr);
+ writew(val, addr);
+}
/*!
- * This function get the value of PHY registers by MII interface
+ * This function gets the value of PHY registers via MII interface
*/
static int
mxc_fec_mii_read(volatile mxc_fec_reg_t *hw_reg, unsigned char phy_addr, unsigned char reg_addr,
- unsigned short int *value)
+ unsigned short int *value)
{
unsigned long waiting = FEC_MII_TIMEOUT;
-
+
if (net_debug) diag_printf("%s: Trying to read phy[%02x] reg %04x\n",
__FUNCTION__, phy_addr, reg_addr);
- if (hw_reg->eir & FEC_EVENT_MII) {
+ if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
if (net_debug) diag_printf("%s: Clearing EIR_EVENT_MII\n", __FUNCTION__);
- hw_reg->eir = FEC_EVENT_MII ;
+ mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
}
- if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, hw_reg->eir);
- hw_reg->mmfr = FEC_MII_READ(phy_addr, reg_addr);/*Write CMD*/
+ if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
+ mxc_fec_reg_write(hw_reg, mmfr, FEC_MII_READ(phy_addr, reg_addr));/* Write CMD */
while (1) {
- if (hw_reg->eir & FEC_EVENT_MII) {
+ if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
if (net_debug) diag_printf("%s: Got EIR_EVENT_MII: EIR=%08lx\n",
- __FUNCTION__, hw_reg->eir);
- hw_reg->eir = FEC_EVENT_MII ;
+ __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
+ mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
break;
}
if (--waiting == 0) {
diag_printf("%s: Read from PHY at addr %d reg 0x%02x timed out: EIR=%08lx\n",
- __FUNCTION__, phy_addr, reg_addr, hw_reg->eir);
+ __FUNCTION__, phy_addr, reg_addr,
+ mxc_fec_reg_read(hw_reg, eir));
return -1;
}
- hal_delay_us(FEC_MII_TICK);
+ hal_delay_us(FEC_MII_TICK);
}
- *value = FEC_MII_GET_DATA(hw_reg->mmfr);
+ *value = FEC_MII_GET_DATA(mxc_fec_reg_read(hw_reg, mmfr));
if (net_debug) diag_printf("%s: Read %04x from phy[%02x] reg %04x\n", __FUNCTION__,
*value, phy_addr, reg_addr);
return 0;
/*!
* This function set the value of PHY registers by MII interface
*/
-static int
-mxc_fec_mii_write(volatile mxc_fec_reg_t * hw_reg, unsigned char phy_addr, unsigned char reg_addr,
+static int
+mxc_fec_mii_write(volatile mxc_fec_reg_t *hw_reg, unsigned char phy_addr, unsigned char reg_addr,
unsigned short int value)
{
unsigned long waiting = FEC_MII_TIMEOUT;
-
+
if (net_debug) diag_printf("%s: Trying to write %04x to phy[%02x] reg %04x\n", __FUNCTION__,
value, phy_addr, reg_addr);
- if(hw_reg->eir & FEC_EVENT_MII ) {
+ if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
if (net_debug) diag_printf("%s: Clearing EIR_EVENT_MII\n", __FUNCTION__);
- hw_reg->eir = FEC_EVENT_MII;
+ mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
}
- if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, hw_reg->eir);
- hw_reg->mmfr = FEC_MII_WRITE(phy_addr, reg_addr, value);/*Write CMD*/
+ if (net_debug) diag_printf("%s: EIR=%08lx\n", __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
+ mxc_fec_reg_write(hw_reg, mmfr, FEC_MII_WRITE(phy_addr, reg_addr, value));/* Write CMD */
if (net_debug) diag_printf("%s: Wrote cmd %08x to MMFR\n", __FUNCTION__,
FEC_MII_WRITE(phy_addr, reg_addr, value));
while (1) {
- if (hw_reg->eir & FEC_EVENT_MII) {
+ if (mxc_fec_reg_read(hw_reg, eir) & FEC_EVENT_MII) {
if (net_debug) diag_printf("%s: Got EIR_EVENT_MII: EIR=%08lx\n",
- __FUNCTION__, hw_reg->eir);
- hw_reg->eir = FEC_EVENT_MII ;
+ __FUNCTION__, mxc_fec_reg_read(hw_reg, eir));
+ mxc_fec_reg_write(hw_reg, eir, FEC_EVENT_MII);
break;
}
if (--waiting == 0) {
diag_printf("%s: Write to PHY at addr %d reg 0x%02x timed out: EIR=%08lx\n",
- __FUNCTION__, phy_addr, reg_addr, hw_reg->eir);
+ __FUNCTION__, phy_addr, reg_addr,
+ mxc_fec_reg_read(hw_reg, eir));
return -1;
}
hal_delay_us(FEC_MII_TICK);
}
static void
-mxc_fec_set_mac_address(volatile mxc_fec_reg_t *dev, unsigned char *enaddr)
+mxc_fec_set_mac_address(volatile mxc_fec_reg_t *hw_reg, unsigned char *enaddr)
{
unsigned long value;
-
+
value = enaddr[0];
value = (value << 8) + enaddr[1];
value = (value << 8) + enaddr[2];
value = (value << 8) + enaddr[3];
- dev->palr = value;
-
+ mxc_fec_reg_write(hw_reg, palr, value);
+
value = enaddr[4];
value = (value << 8) + enaddr[5];
- dev->paur = (value << 16);
+ mxc_fec_reg_write(hw_reg, paur, value << 16);
}
/*!
- * This function set the value of PHY registers by MII interface
+ * This function enables the FEC for reception of packets
*/
-static void
+static void
mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
- mxc_fec_priv_t *priv = sc?sc->driver_private:NULL;
- volatile mxc_fec_reg_t *chip = priv?priv->hw_reg:NULL;
+ mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
+ volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
- if (!(priv && chip) || enaddr == NULL ) {
- diag_printf("BUG[start]: MAC address or some fields in driver is NULL\n");
+ if (!(priv && hw_reg)) {
+ diag_printf("BUG[start]: FEC driver not initialized\n");
+ return;
+ }
+ if (enaddr == NULL) {
+ diag_printf("BUG[start]: no MAC address supplied\n");
return;
}
- mxc_fec_set_mac_address(chip, enaddr);
+ mxc_fec_set_mac_address(hw_reg, enaddr);
priv->tx_busy = 0;
- chip->ecr |= FEC_ETHER_EN;
- chip->rdar |= FEC_RX_TX_ACTIVE;
+ mxc_fec_reg_write(hw_reg, rdar, mxc_fec_reg_read(hw_reg, rdar) | FEC_RX_TX_ACTIVE);
+ mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_ETHER_EN);
+#ifdef CYGPKG_HAL_ARM_MX25
+ /*
+ * setup the MII gasket for RMII mode
+ */
+
+ /* disable the gasket */
+ mxc_fec_reg_write16(hw_reg, miigsk_enr, 0);
+
+ /* wait for the gasket to be disabled */
+ while (mxc_fec_reg_read16(hw_reg, miigsk_enr) & MIIGSK_ENR_READY)
+ hal_delay_us(FEC_COMMON_TICK);
+
+ /* configure gasket for RMII, 50 MHz, no loopback, and no echo */
+ mxc_fec_reg_write16(hw_reg, miigsk_cfgr, MIIGSK_CFGR_IF_MODE_RMII);
+
+ /* re-enable the gasket */
+ mxc_fec_reg_write16(hw_reg, miigsk_enr, MIIGSK_ENR_EN);
+
+ /* wait until MII gasket is ready */
+ int max_loops = 10;
+ while ((mxc_fec_reg_read16(hw_reg, miigsk_enr) & MIIGSK_ENR_READY) == 0) {
+ if (--max_loops <= 0) {
+ diag_printf("WAIT for MII Gasket ready timed out\n");
+ break;
+ }
+ }
+#endif
}
/*!
* This function pauses the FEC controller.
*/
-static void
+static void
mxc_fec_stop(struct eth_drv_sc *sc)
{
- mxc_fec_priv_t *priv = sc?sc->driver_private:NULL;
- volatile mxc_fec_reg_t *chip = priv?priv->hw_reg:NULL;
- if (!(priv && chip)) {
- diag_printf("BUG[stop]: some fields in driver is NULL\n");
+ mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
+ volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
+
+ if (!(priv && hw_reg)) {
+ diag_printf("BUG[stop]: FEC driver not initialized\n");
return;
}
- chip->ecr &= ~FEC_ETHER_EN;
+ mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) & ~FEC_ETHER_EN);
}
-static int
+static int
mxc_fec_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length)
{
/*TODO:: Add support */
/*!
* This function checks the status of FEC control.
*/
-static int
+static int
mxc_fec_can_send(struct eth_drv_sc *sc)
{
- mxc_fec_priv_t *priv = sc?sc->driver_private:NULL;
- volatile mxc_fec_reg_t *hw_reg = priv?priv->hw_reg:NULL;
+ mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
+ volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
if (!(priv && hw_reg)) {
- diag_printf("BUG[can_send]:the private pointer and register pointer in MXC_FEC is NULL\n");
+ diag_printf("BUG[can_send]: FEC driver not initialized\n");
return 0;
}
if (priv->tx_busy) {
- diag_printf("WARNING[can_send]: MXC_FEC is busy for transmittinig\n");
+ diag_printf("WARNING[can_send]: MXC_FEC is busy for transmission\n");
return 0;
}
- if (!(hw_reg->ecr & FEC_ETHER_EN)) {
+ if (!(mxc_fec_reg_read(hw_reg, ecr) & FEC_ETHER_EN)) {
diag_printf("WARNING[can_send]: MXC_FEC is not enabled\n");
return 0;
}
- if (hw_reg->tcr & FEC_TCR_RFC_PAUSE) {
+ if (mxc_fec_reg_read(hw_reg, tcr) & FEC_TCR_RFC_PAUSE) {
diag_printf("WARNING[can_send]: MXC_FEC is paused\n");
return 0;
}
/*!
* This function transmits a frame.
*/
-static void
+static void
mxc_fec_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total,
- unsigned long key)
+ unsigned long key)
{
- mxc_fec_priv_t *dev = sc?sc->driver_private:NULL;
- volatile mxc_fec_reg_t *hw_reg = dev?dev->hw_reg:NULL;
+ mxc_fec_priv_t *dev = sc ? sc->driver_private : NULL;
+ volatile mxc_fec_reg_t *hw_reg = dev ? dev->hw_reg : NULL;
mxc_fec_bd_t *p;
int i, off;
- if ( dev == NULL || hw_reg == NULL) {
- diag_printf("BUG[TX]: some fields in driver are NULL\n");
+ if (dev == NULL || hw_reg == NULL) {
+ diag_printf("BUG[TX]: FEC driver not initialized\n");
return;
}
- if ( total > (FEC_FRAME_LEN-4)) total = FEC_FRAME_LEN-4;
- if ( sg_list == NULL || total <= 14 ) {
- if(sc->funs->eth_drv && sc->funs->eth_drv->tx_done) {
+ if (total > (FEC_FRAME_LEN - 4)) total = FEC_FRAME_LEN - 4;
+ if (sg_list == NULL || total <= 14) {
+ if (sc->funs->eth_drv && sc->funs->eth_drv->tx_done) {
sc->funs->eth_drv->tx_done(sc, key, -1);
}
return;
- }
+ }
- for(i=0, off=0, p = dev->tx_cur; i<sg_len; i++) {
+ for (i = 0, off = 0, p = dev->tx_cur; i < sg_len; i++) {
unsigned long vaddr;
- if(p->status & BD_TX_ST_RDY) {
- diag_printf("BUG[TX]:MXC_FEC's status=%x\n", p->status);
+
+ if (p->status & BD_TX_ST_RDY) {
+ diag_printf("BUG[TX]: trying to resend already finished buffer\n");
break;
}
if (sg_list[i].buf == 0) {
memcpy((void *)vaddr, (void *)sg_list[i].buf, sg_list[i].len);
off += sg_list[i].len;
}
- if ( off < 14 ) {
- diag_printf("WARNING[TX]: data len is %d\n", off);
+ if (off < 14) {
+ diag_printf("WARNING[TX]: packet size %d too small\n", off);
return;
}
- p->length = off;
- p->status &= ~(BD_TX_ST_LAST|BD_TX_ST_RDY|BD_TX_ST_TC|BD_TX_ST_ABC);
- p->status |= BD_TX_ST_LAST| BD_TX_ST_RDY | BD_TX_ST_TC;
- if(p->status & BD_TX_ST_WRAP ) {
+ p->length = off;
+ //p->status &= ~(BD_TX_ST_LAST | BD_TX_ST_RDY | BD_TX_ST_TC | BD_TX_ST_ABC);
+ p->status &= ~BD_TX_ST_ABC;
+ p->status |= BD_TX_ST_LAST | BD_TX_ST_RDY | BD_TX_ST_TC;
+ if (p->status & BD_TX_ST_WRAP) {
p = dev->tx_bd;
- } else p++;
+ } else {
+ p++;
+ }
dev->tx_cur = p;
dev->tx_busy = 1;
dev->tx_key = key;
- hw_reg->tdar = FEC_RX_TX_ACTIVE;
+ mxc_fec_reg_write(hw_reg, tdar, FEC_RX_TX_ACTIVE);
}
/*!
* This function receives ready Frame in DB.
*/
-static void
+static void
mxc_fec_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
- mxc_fec_priv_t * priv = sc?sc->driver_private:NULL;
- mxc_fec_bd_t * p;
+ mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
+ mxc_fec_bd_t *p;
unsigned long vaddr;
- if(sg_list == NULL || priv == NULL || sg_len <= 0) {
- diag_printf("BUG[RX]: driver's private field or argument of this calling is NULL \n");
+
+ if (sg_list == NULL || priv == NULL || sg_len <= 0) {
+ diag_printf("BUG[RX]: FEC driver not initialized\n");
return;
}
-
- /*TODO: I think if buf pointer is NULL, this function
+
+ /*TODO: I think if buf pointer is NULL, this function
* should not be called
*/
- if(sg_list->buf == 0) {
+ if (sg_list->buf == 0) {
diag_printf("WARING[RX]: the sg_list is empty\n");
return;
}
p = priv->rx_cur;
-
- if(p->status & BD_RX_ST_EMPTY) {
- diag_printf("BUG[RX]: status =%x\n", p->status);
+
+ if (p->status & BD_RX_ST_EMPTY) {
+ diag_printf("BUG[RX]: empty buffer received; status=%04x\n", p->status);
return;
}
- if(!(p->status & BD_RX_ST_LAST)) {
- diag_printf("BUG[RX]: status =%x\n", p->status);
+ if (!(p->status & BD_RX_ST_LAST)) {
+ diag_printf("BUG[RX]: status=%0xx\n", p->status);
return;
}
vaddr = hal_ioremap_nocache((unsigned long)p->data);
/*TODO::D_CACHE invalid this data buffer*/
- memcpy((void *)sg_list->buf, (void *)vaddr, p->length -4);
+ memcpy((void *)sg_list->buf, (void *)vaddr, p->length - 4);
}
-static void
+static void
mxc_fec_deliver(struct eth_drv_sc *sc)
{
- /*TODO::When redboot support thread ,
+ /*TODO::When redboot support thread ,
* the polling function will be called at here
*/
return;
}
-static void
-mxc_fec_check_rx_bd(struct eth_drv_sc * sc)
+/* This funtion just called by polling funtion */
+static void
+mxc_fec_check_rx_bd(struct eth_drv_sc *sc)
{
- /* This funtion just called by polling funtion*/
- mxc_fec_priv_t * priv = sc->driver_private;
- mxc_fec_bd_t * p;
- volatile mxc_fec_reg_t * hw_reg = priv->hw_reg;
+ mxc_fec_priv_t *priv = sc->driver_private;
+ mxc_fec_bd_t *p;
+ volatile mxc_fec_reg_t *hw_reg = priv->hw_reg;
int i;
-
- for(i = 0, p = priv->rx_cur; i< FEC_RX_FRAMES; i++){
- /*TODO::D-CACHE invalid this BD.
- *In WRITE_BACK mode: this maybe destroy the next BD
- * when the CACHE_LINE write back.
- */
- if(p->status & BD_RX_ST_EMPTY) {
+
+ for (i = 0, p = priv->rx_cur; i < FEC_RX_FRAMES; i++) {
+ /*
+ * TODO::D-CACHE invalidate this BD.
+ * In WRITE_BACK mode: this may destroy the next BD
+ * when the CACHE_LINE is written back.
+ */
+ if (p->status & BD_RX_ST_EMPTY) {
break;
}
- if(!(p->status & BD_RX_ST_LAST)) {
- diag_printf("BUG[RX]: status=%x, length=%x\n", p->status, p->length);
+ if (!(p->status & BD_RX_ST_LAST)) {
+ diag_printf("BUG[RX]: status=%04x, length=%x\n", p->status, p->length);
goto skip_next;
}
-
- if((p->status & BD_RX_ST_ERRS)|| (p->length > FEC_FRAME_LEN)) {
- diag_printf("BUG[RX]: status=%x, length=%x\n", p->status, p->length);
+
+ if (p->status & BD_RX_ST_ERRS) {
+ diag_printf("RX error: status=%08x errors=%08x\n", p->status,
+ p->status & BD_RX_ST_ERRS);
+ } else if (p->length > FEC_FRAME_LEN) {
+ diag_printf("RX error: packet size 0x%08x larger than max frame length: 0x%08x\n",
+ p->length, FEC_FRAME_LEN);
} else {
- sc->funs->eth_drv->recv(sc, p->length -4);
+ sc->funs->eth_drv->recv(sc, p->length - 4);
}
skip_next:
p->status = (p->status & BD_RX_ST_WRAP) | BD_RX_ST_EMPTY;
-
- if ( p->status & BD_RX_ST_WRAP) {
+
+ if (p->status & BD_RX_ST_WRAP) {
p = priv->rx_bd;
} else {
p++;
- }
- priv->rx_cur = p;
- hw_reg->ecr |= FEC_ETHER_EN;
- hw_reg->rdar |= FEC_RX_TX_ACTIVE;
+ }
+ priv->rx_cur = p;
+ mxc_fec_reg_write(hw_reg, rdar, mxc_fec_reg_read(hw_reg, rdar) | FEC_RX_TX_ACTIVE);
}
}
/*!
* This function checks the event of FEC controller
*/
-static void
-mxc_fec_poll(struct eth_drv_sc * sc)
+static void
+mxc_fec_poll(struct eth_drv_sc *sc)
{
- mxc_fec_priv_t * priv = sc?sc->driver_private:NULL;
- volatile mxc_fec_reg_t * hw_reg = priv?priv->hw_reg:NULL;
- unsigned long value;
+ mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
+ volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
+ unsigned long value;
+ int dbg = net_debug;
- if ( priv == NULL || hw_reg == NULL) {
- diag_printf("BUG[POLL]: some fields in driver are NULL\n");
+ if (priv == NULL || hw_reg == NULL) {
+ diag_printf("BUG[POLL]: FEC driver not initialized\n");
return;
}
- value = hw_reg->eir;
- hw_reg->eir = value & (~FEC_EVENT_MII);
-
- if(value&FEC_EVENT_TX_ERR) {
- diag_printf("WARNING[POLL]: There are errors(%lu) for transmit\n",
- value&FEC_EVENT_TX_ERR);
+#if 1
+ net_debug = 0;
+#endif
+ value = mxc_fec_reg_read(hw_reg, eir);
+ mxc_fec_reg_write(hw_reg, eir, value & ~FEC_EVENT_MII);
+#if 1
+ net_debug = dbg;
+#endif
+ if (value & FEC_EVENT_TX_ERR) {
+ diag_printf("WARNING[POLL]: Transmit error\n");
sc->funs->eth_drv->tx_done(sc, priv->tx_key, -1);
priv->tx_busy = 0;
} else {
- if(value&FEC_EVENT_TX) {
- sc->funs->eth_drv->tx_done(sc, priv->tx_key, 0);
+ if (value & FEC_EVENT_TX) {
+ sc->funs->eth_drv->tx_done(sc, priv->tx_key, 0);
priv->tx_busy = 0;
}
}
-
- if(value&FEC_EVENT_RX) {
+
+ if (value & FEC_EVENT_RX) {
mxc_fec_check_rx_bd(sc);
}
- if(value & FEC_EVENT_HBERR) {
+ if (value & FEC_EVENT_HBERR) {
diag_printf("WARNGING[POLL]: Hearbeat error!\n");
}
- if(value & FEC_EVENT_EBERR) {
+ if (value & FEC_EVENT_EBERR) {
diag_printf("WARNING[POLL]: Ethernet Bus Error!\n");
}
}
-
static int
mxc_fec_int_vector(struct eth_drv_sc *sc)
{
/*TODO::
- * get FEC interrupt number
+ * get FEC interrupt number
*/
return -1;
}
* The function initializes the description buffer for receiving or transmitting
*/
static void
-mxc_fec_bd_init(mxc_fec_priv_t * dev)
+mxc_fec_bd_init(mxc_fec_priv_t *dev)
{
int i;
- mxc_fec_bd_t * p;
+ mxc_fec_bd_t *p;
p = dev->rx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_rx_bd));
- for(i=0; i<FEC_BD_RX_NUM; i++, p++){
+ for (i = 0; i < FEC_BD_RX_NUM; i++, p++) {
p->status = BD_RX_ST_EMPTY;
p->length = 0;
p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_rx_buf[i]);
}
- dev->rx_bd[i-1].status |= BD_RX_ST_WRAP;
+ dev->rx_bd[i - 1].status |= BD_RX_ST_WRAP;
dev->rx_cur = dev->rx_bd;
- p = dev->tx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_tx_bd));
- for(i=0; i<FEC_BD_TX_NUM; i++, p++){
- p->status = 0;
- p->length = 0;
- p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_tx_buf[i]);
- }
+ p = dev->tx_bd = (void *)hal_ioremap_nocache(hal_virt_to_phy((unsigned long)mxc_fec_tx_bd));
+ for (i = 0; i < FEC_BD_TX_NUM; i++, p++) {
+ p->status = 0;
+ p->length = 0;
+ p->data = (void *)hal_virt_to_phy((unsigned long)mxc_fec_tx_buf[i]);
+ }
- dev->tx_bd[i-1].status |= BD_TX_ST_WRAP;
+ dev->tx_bd[i - 1].status |= BD_TX_ST_WRAP;
dev->tx_cur = dev->tx_bd;
-
+
/*TODO:: add the sync function for items*/
}
/*!
- *This function initializes FEC controller.
+ *This function initializes FEC controller.
*/
-static void
-mxc_fec_chip_init(mxc_fec_priv_t * dev)
+static void
+mxc_fec_chip_init(mxc_fec_priv_t *dev)
{
- volatile mxc_fec_reg_t * chip = dev->hw_reg;
+ volatile mxc_fec_reg_t *hw_reg = dev->hw_reg;
unsigned long ipg_clk;
- chip->ecr = FEC_RESET;
- while(chip->ecr & FEC_RESET) {
+ mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_RESET);
+ while (mxc_fec_reg_read(hw_reg, ecr) & FEC_RESET) {
hal_delay_us(FEC_COMMON_TICK);
}
- chip->eimr = 0x00000000;
- chip->eir = 0xFFFFFFFF;
-
- chip->rcr = (chip->rcr&~(0x0000003F))|FEC_RCR_FCE|FEC_RCR_MII_MODE;
- chip->tcr |= FEC_TCR_FDEN;
- chip->mibc |= FEC_MIB_DISABLE;
-
- chip->iaur = 0;
- chip->ialr = 0;
- chip->gaur = 0;
- chip->galr = 0;
+ mxc_fec_reg_write(hw_reg, eimr, 0);
+ mxc_fec_reg_write(hw_reg, eir, ~0);
+
+ mxc_fec_reg_write(hw_reg, rcr,
+ (mxc_fec_reg_read(hw_reg, rcr) & ~0x3F) |
+ FEC_RCR_FCE | FEC_RCR_MII_MODE);
+
+ mxc_fec_reg_write(hw_reg, tcr, mxc_fec_reg_read(hw_reg, tcr) | FEC_TCR_FDEN);
+ mxc_fec_reg_write(hw_reg, mibc, mxc_fec_reg_read(hw_reg, mibc) | FEC_MIB_DISABLE);
+
+ mxc_fec_reg_write(hw_reg, iaur, 0);
+ mxc_fec_reg_write(hw_reg, ialr, 0);
+ mxc_fec_reg_write(hw_reg, gaur, 0);
+ mxc_fec_reg_write(hw_reg, galr, 0);
- /*TODO:: Use MII_SPEED(IPG_CLK) to get the value*/
ipg_clk = get_main_clock(IPG_CLK);
-
- chip->mscr = (chip->mscr & 0x7e) | (((ipg_clk + 499999) / 2500000 / 2) << 1);
- if (net_debug) diag_printf("mscr set to %08lx for ipg_clk %ld\n", chip->mscr,
- ipg_clk);
- /*Enable ETHER_EN*/
- chip->emrbr = 2048-16;
- chip->erdsr = hal_virt_to_phy((unsigned long)dev->rx_bd);
- chip->etdsr = hal_virt_to_phy((unsigned long)dev->tx_bd);
+
+ mxc_fec_reg_write(hw_reg, mscr,
+ (mxc_fec_reg_read(hw_reg, mscr) & ~0x7e) |
+ (((ipg_clk + 499999) / 2500000 / 2) << 1));
+ if (net_debug) diag_printf("mscr set to %08lx for ipg_clk %ld\n",
+ mxc_fec_reg_read(hw_reg, mscr), ipg_clk);
+
+ mxc_fec_reg_write(hw_reg, emrbr, 2048 - 16);
+ mxc_fec_reg_write(hw_reg, erdsr, hal_virt_to_phy((unsigned long)dev->rx_bd));
+ mxc_fec_reg_write(hw_reg, etdsr, hal_virt_to_phy((unsigned long)dev->tx_bd));
+
+ /* must be done before enabling the MII gasket
+ * (otherwise MIIGSK_ENR_READY will never assert)
+ */
+ mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_ETHER_EN);
}
static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool show)
return;
}
if (dev->status & FEC_STATUS_LINK_ON) {
- diag_printf("FEC: [ %s ] [ %s ]:\n",
+ diag_printf("FEC: [ %s ] [ %s ]:\n",
(dev->status & FEC_STATUS_FULL_DPLX) ? "FULL_DUPLEX" : "HALF_DUPLEX",
(dev->status & FEC_STATUS_100M) ? "100 Mbps" : "10 Mbps");
} else {
#ifndef CYGPKG_DEVS_ETH_PHY
/*!
- * This function initialize PHY
+ * This function initializes the PHY
*/
static bool
mxc_fec_phy_init(mxc_fec_priv_t *dev)
{
+#if 1
unsigned short value = 0;
- unsigned long timeout=FEC_COMMON_TIMEOUT;
+ unsigned long timeout = FEC_COMMON_TIMEOUT;
+
/*Reset PHY*/
mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, PHY_CTRL_RESET);
while (timeout--) {
return false;
}
- if(!(value & PHY_CTRL_RESET)) {
+ if (!(value & PHY_CTRL_RESET)) {
if (net_debug) diag_printf("%s: FEC reset completed\n", __FUNCTION__);
break;
}
id = (value & PHY_ID1_MASK) << PHY_ID1_SHIFT;
mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_2, &value);
id |= (value & PHY_ID2_MASK) << PHY_ID2_SHIFT;
- if( id == 0 || id == 0xffffffff) {
+ if (id == 0 || id == 0xffffffff) {
diag_printf("FEC could not identify PHY: ID=%08lx\n", id);
return false;
}
hal_delay_us(FEC_MII_TICK);
}
mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, &value);
- value &= ~(PHY_LED_SEL);
+ value &= ~PHY_LED_SEL;
mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, value);
+#else
+ unsigned long value = 0;
+ unsigned long id = 0, timeout = 50;
+
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_1, &value);
+ id = (value & PHY_ID1_MASK) << PHY_ID1_SHIFT;
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_IDENTIFY_2, &value);
+ id |= (value & PHY_ID2_MASK) << PHY_ID2_SHIFT;
+
+ switch (id) {
+ case 0x00540088:
+ break;
+ case 0x00007C0C:
+ break;
+ default:
+ diag_printf("[Warning] FEC not connect right PHY: ID=%lx\n", id);
+ }
+
+ mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, PHY_CTRL_AUTO_NEG|PHY_CTRL_FULL_DPLX);
+
+#ifdef CYGPKG_HAL_ARM_MX27ADS
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, &value);
+ value &= ~PHY_LED_SEL;
+ mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_MODE_REG, value);
+#endif
+
+#if defined(CYGPKG_HAL_ARM_MX51) || defined (CYGPKG_HAL_ARM_MX25_3STACK) || defined (CYGPKG_HAL_ARM_MX35_3STACK) || defined (CYGPKG_HAL_ARM_MX27_3STACK)
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_AUTO_NEG_EXP_REG, &value);
+ /* Wait for packet to arrive */
+ while (((value & PHY_AUTO_NEG_NEW_PAGE) == 0) && (timeout != 0)) {
+ hal_delay_us(100);
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_AUTO_NEG_EXP_REG, &value);
+ timeout--;
+ }
+ /* Check if link is capable of auto-negotiation */
+ if ((value & PHY_AUTO_NEG_CAP) == 1) {
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_INT_SRC_REG, &value);
+ timeout = 50;
+ /* Wait for auto-negotiation to complete */
+ while (((value & PHY_INT_AUTO_NEG) == 0) && (timeout != 0)) {
+ hal_delay_us(100);
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_INT_SRC_REG, &value);
+ timeout--;
+ }
+ }
+#endif
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value);
+ if (value & PHY_STATUS_LINK_ST) {
+ dev->status |= FEC_STATUS_LINK_ON;
+ } else {
+ dev->status &= ~FEC_STATUS_LINK_ON;
+ }
+
+#ifdef CYGPKG_HAL_ARM_MX27ADS
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_DIAG_REG, &value);
+ if (value & PHY_DIAG_DPLX) {
+ dev->status |= FEC_STATUS_FULL_DPLX;
+ } else {
+ dev->status &= ~FEC_STATUS_FULL_DPLX;
+ }
+ if (value & PHY_DIAG_DPLX) {
+ dev->status |= FEC_STATUS_100M;
+ } else {
+ dev->status &= ~FEC_STATUS_100M;
+ }
+#endif
+
+#if defined(CYGPKG_HAL_ARM_MX51) || defined (CYGPKG_HAL_ARM_MX25_3STACK) || defined (CYGPKG_HAL_ARM_MX35_3STACK)
+ mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_AUTO_NEG_REG, &value);
+ if (value & PHY_AUTO_10BASET) {
+ dev->status &= ~FEC_STATUS_100M;
+ if (value & PHY_AUTO_10BASET_DPLX) {
+ dev->status |= FEC_STATUS_FULL_DPLX;
+ } else {
+ dev->status &= ~FEC_STATUS_FULL_DPLX;
+ }
+ }
+ if (value & PHY_AUTO_100BASET) {
+ dev->status |= FEC_STATUS_100M;
+ if (value & PHY_AUTO_100BASET_DPLX) {
+ dev->status |= FEC_STATUS_FULL_DPLX;
+ } else {
+ dev->status &= ~FEC_STATUS_FULL_DPLX;
+ }
+ }
+#endif
+ diag_printf("FEC: [ %s ] [ %s ] [ %s ]:\n",
+ (dev->status&FEC_STATUS_FULL_DPLX)?"FULL_DUPLEX":"HALF_DUPLEX",
+ (dev->status&FEC_STATUS_LINK_ON)?"connected":"disconnected",
+ (dev->status&FEC_STATUS_100M)?"100M bps":"10M bps");
+#endif
return true;
}
}
if (id == 0) {
/* Disable MII */
- fep->hw_reg->mscr = 0;
+ fep->mxc_fec_reg_write(hw_reg, mscr, 0);
ret = -1;
}
unsigned long timeout=FEC_COMMON_TIMEOUT;
mxc_fec_priv_t *dev = &mxc_fec_private;
- /*Reset PHY*/
+ /* Reset PHY */
if (net_debug) diag_printf("%s\n", __FUNCTION__);
_eth_phy_write(dev->phy, PHY_CTRL_REG, dev->phy->phy_addr, PHY_CTRL_RESET);
return;
}
- if(!(value & PHY_CTRL_RESET)) {
+ if (!(value & PHY_CTRL_RESET)) {
if (net_debug) diag_printf("%s: FEC reset completed\n", __FUNCTION__);
break;
}
mxc_fec_mii_write(mxc_fec_private.hw_reg, unit, reg, data);
}
-/*! This function initializes the FEC driver.
+/*! This function initializes the FEC driver.
* It is called by net_init in net module of RedBoot during RedBoot init
*/
-static bool
+static bool
mxc_fec_init(struct cyg_netdevtab_entry *tab)
{
- struct eth_drv_sc * sc = tab ? tab->device_instance : NULL;
- mxc_fec_priv_t * private;
- char eth_add_local[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ struct eth_drv_sc *sc = tab ? tab->device_instance : NULL;
+ mxc_fec_priv_t *private;
+ unsigned char eth_add_local[ETHER_ADDR_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int ok = 0;
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
cyg_bool set_esa;
- int ok;
-
- /* Get MAC address */
- ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
- "fec_esa", &set_esa, CONFIG_BOOL);
- if (ok && set_esa) {
- CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
- "fec_esa_data", eth_add_local, CONFIG_ESA);
- }
#endif
+
if (net_debug) diag_printf("%s:\n", __FUNCTION__);
- if (sc == NULL){
+ if (sc == NULL) {
diag_printf("%s: no driver attached\n", __FUNCTION__);
return false;
- }
-
+ }
+
private = MXC_FEC_PRIVATE(sc);
if (private == NULL) {
- private = MXC_FEC_PRIVATE(sc) = &mxc_fec_private;
+ private = &mxc_fec_private;
+ }
+ if (private->provide_esa) {
+ ok = private->provide_esa(eth_add_local);
+ }
+#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
+ if (!ok) {
+ /* Get MAC address from fconfig */
+ ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
+ "fec_esa", &set_esa, CONFIG_BOOL);
+ if (ok && set_esa) {
+ CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
+ "fec_esa_data", eth_add_local, CONFIG_ESA);
+ }
+ }
+#endif
+ if (!ok) {
+ diag_printf("No ESA provided via fuses or RedBoot config\n");
+ return false;
}
private->hw_reg = (void *)SOC_FEC_BASE;
}
private->phy_addr = ok;
mxc_fec_phy_init(private);
-#endif
- /*TODO:: initialize System Resource : irq, timer */
+#endif
+ /* TODO:: initialize System Resource : irq, timer */
sc->funs->eth_drv->init(sc, eth_add_local);
mxc_fec_phy_status(private, _eth_phy_state(private->phy), true);
-
- return true;
+
+ return true;
}
#ifndef CYGPKG_DEVS_ETH_PHY
/*!
- * Global variable which defines the FEC driver,
+ * Global variable which defines the FEC driver,
*/
ETH_DRV_SC(mxc_fec_sc,
- &mxc_fec_private, // Driver specific data
+ &mxc_fec_private, // Driver specific data
mxc_fec_name,
mxc_fec_start,
mxc_fec_stop,
mxc_fec_name,
mxc_fec_init,
&mxc_fec_sc);
-#endif
+
+#endif // CYGPKG_DEVS_ETH_PHY