summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/m68k/genmcf548x
diff options
context:
space:
mode:
authorThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-10-15 16:02:12 +0000
committerThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-10-15 16:02:12 +0000
commit3a0c0f848307ef3d15c985b0aa5bb19f28484b37 (patch)
tree2b5d0f38ec82ba17c9a497648f57b8bc5153a381 /c/src/lib/libbsp/m68k/genmcf548x
parent2009-10-15 Ralf Corsépius <ralf.corsepius@rtems.org> (diff)
downloadrtems-3a0c0f848307ef3d15c985b0aa5bb19f28484b37.tar.bz2
add PHY control support
Diffstat (limited to 'c/src/lib/libbsp/m68k/genmcf548x')
-rw-r--r--c/src/lib/libbsp/m68k/genmcf548x/ChangeLog7
-rw-r--r--c/src/lib/libbsp/m68k/genmcf548x/network/network.c343
2 files changed, 159 insertions, 191 deletions
diff --git a/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog b/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog
index f756488aa4..63e488a0e3 100644
--- a/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog
+++ b/c/src/lib/libbsp/m68k/genmcf548x/ChangeLog
@@ -1,7 +1,12 @@
2009-10-15 Thomas Doerfler <Thomas.Doerfler@embedded-brains.de>
+ * network/network.c: add PHY control support
+
+2009-10-15 Thomas Doerfler <Thomas.Doerfler@embedded-brains.de>
+
* Makefile.am, include/bsp.h, network/network.c: add network support
- * console/console.c, clock/clock.c, startup/init548x.c:
+ * console/console.c, clock/clock.c, startup/init548x.c,
+ * README:
various corrections
2009-10-15 Ralf Corsépius <ralf.corsepius@rtems.org>
diff --git a/c/src/lib/libbsp/m68k/genmcf548x/network/network.c b/c/src/lib/libbsp/m68k/genmcf548x/network/network.c
index eb038e8008..d20f7f772a 100644
--- a/c/src/lib/libbsp/m68k/genmcf548x/network/network.c
+++ b/c/src/lib/libbsp/m68k/genmcf548x/network/network.c
@@ -51,6 +51,7 @@
#include <bsp.h>
#include <mcf548x/mcf548x.h>
+#include <rtems/rtems_mii_ioctl.h>
#include <errno.h>
/* freescale-api-specifics... */
@@ -64,6 +65,7 @@
*/
#define NIFACES 2
+#define FEC_WATCHDOG_TIMEOUT 5 /* check media every 5 seconds */
/*
* buffer descriptor handling
*/
@@ -106,7 +108,7 @@ extern char _SysSramBase[];
#define SRAM_DMA_BASE(base) ((void *)SRAM_RXBD_BASE(base,NIFACES+1))
-#define ETH_DEBUG
+#undef ETH_DEBUG
/*
* Default number of buffer descriptors set aside for this driver.
@@ -216,6 +218,14 @@ struct mcf548x_enet_struct {
rtems_id rxDaemonTid;
rtems_id txDaemonTid;
+ /*
+ * MDIO/Phy info
+ */
+ struct rtems_mdio_info mdio_info;
+ int phy_default;
+ int phy_chan; /* which fec channel services this phy access? */
+ int media_state; /* (last detected) state of media */
+
unsigned long rxInterrupts;
unsigned long rxNotLast;
unsigned long rxGiant;
@@ -408,15 +418,20 @@ static void mcf548x_eth_addr_filter_set(struct mcf548x_enet_struct *sc) {
* 18-wire ethernet tranceiver (PHY). Please see your PHY
* documentation for the register map.
*
- * Returns: 32-bit register value
+ * Returns: 0 if ok
*
* Notes:
*
*/
-int mcf548x_eth_mii_read(struct mcf548x_enet_struct *sc, unsigned char phyAddr, unsigned char regAddr, unsigned short * retVal)
- {
+int mcf548x_eth_mii_read(
+ int phyAddr, /* PHY number to access or -1 */
+ void *uarg, /* unit argument */
+ unsigned regAddr, /* register address */
+ uint32_t *retVal) /* ptr to read buffer */
+{
+ struct mcf548x_enet_struct *sc = uarg;
int timeout = 0xffff;
- int chan = sc->chan;
+ int chan = sc->phy_chan;
/*
* reading from any PHY's register is done by properly
@@ -433,16 +448,15 @@ int mcf548x_eth_mii_read(struct mcf548x_enet_struct *sc, unsigned char phyAddr,
*/
while ((timeout--) && (!(MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_MII)));
- if(timeout == 0)
- {
+ if(timeout == 0) {
#ifdef ETH_DEBUG
- printf ("Read MDIO failed..." "\r\n");
+ iprintf ("Read MDIO failed..." "\r\n");
#endif
- return false;
+ return 1;
- }
+ }
/*
* clear mii interrupt bit
@@ -454,9 +468,9 @@ int mcf548x_eth_mii_read(struct mcf548x_enet_struct *sc, unsigned char phyAddr,
*/
*retVal = (unsigned short) MCF548X_FEC_MMFR(chan);
- return true;
+ return 0;
- }
+}
/*
* Function: mcf548x_eth_mii_write
@@ -470,10 +484,15 @@ int mcf548x_eth_mii_read(struct mcf548x_enet_struct *sc, unsigned char phyAddr,
* Notes:
*
*/
-static int mcf548x_eth_mii_write(struct mcf548x_enet_struct *sc, unsigned char phyAddr, unsigned char regAddr, unsigned short data)
- {
- int chan = sc->chan;
- int timeout = 0xffff;
+static int mcf548x_eth_mii_write(
+ int phyAddr, /* PHY number to access or -1 */
+ void *uarg, /* unit argument */
+ unsigned regAddr, /* register address */
+ uint32_t data) /* write data */
+{
+ struct mcf548x_enet_struct *sc = uarg;
+ int chan = sc->phy_chan;
+ int timeout = 0xffff;
MCF548X_FEC_MMFR(chan) = (MCF548X_FEC_MMFR_ST_01 |
MCF548X_FEC_MMFR_OP_WRITE |
@@ -491,10 +510,10 @@ static int mcf548x_eth_mii_write(struct mcf548x_enet_struct *sc, unsigned char p
{
#ifdef ETH_DEBUG
- printf ("Write MDIO failed..." "\r\n");
+ iprintf ("Write MDIO failed..." "\r\n");
#endif
- return false;
+ return 1;
}
@@ -503,7 +522,7 @@ static int mcf548x_eth_mii_write(struct mcf548x_enet_struct *sc, unsigned char p
*/
MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_MII;
- return true;
+ return 0;
}
@@ -569,22 +588,29 @@ void mcf548x_fec_off(struct mcf548x_enet_struct *sc)
#if defined(ETH_DEBUG)
- unsigned short phyStatus, i;
- unsigned char phyAddr = 0;
+ uint32_t phyStatus;
+ int i;
for(i = 0; i < 9; i++)
{
- mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus);
- printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus);
+ mcf548x_eth_mii_read(sc->phy_default, sc, i, &phyStatus);
+ iprintf ("Mii reg %d: 0x%04lx" "\r\n", i, phyStatus);
}
for(i = 16; i < 21; i++)
{
- mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus);
- printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus);
+ mcf548x_eth_mii_read(sc->phy_default, sc, i, &phyStatus);
+ iprintf ("Mii reg %d: 0x%04lx" "\r\n", i, phyStatus);
+
+ }
+ for(i = 0; i < 32; i++)
+ {
+
+ mcf548x_eth_mii_read(i, sc, 0, &phyStatus);
+ iprintf ("Mii Phy=%d, reg 0: 0x%04lx" "\r\n", i, phyStatus);
}
#endif /* ETH_DEBUG */
@@ -746,39 +772,6 @@ static void mcf548x_fec_retire_tbd(struct mcf548x_enet_struct *sc,
}
}
-#if 0
- /*
- * Function: mcf548x_fec_tx_bd_requeue
- *
- * Description: put buffers back to interface output queue
- *
- * Returns: void
- *
- * Notes:
- *
- */
-static void mcf548x_fec_tx_bd_requeue(struct mcf548x_enet_struct *sc)
-{
- /*
- * Clear already transmitted BDs first. Will not work calling same
- * from fecExceptionHandler(TFINT).
- */
-
- while (sc->txBdActiveCount > 0) {
- if (sc->txMbuf[sc->txBdHead] != NULL) {
- /*
- * NOTE: txMbuf can be NULL, if mbuf has been split into different BDs
- */
- IF_PREPEND(&(sc->arpcom.ac_if.if_snd),sc->txMbuf[sc->txBdHead]);
- sc->txMbuf[sc->txBdHead] = NULL;
- }
- sc->txBdActiveCount--;
- if(--sc->txBdHead < 0) {
- sc->txBdHead = sc->txBdCount-1;
- }
- }
-}
-#endif
static void mcf548x_fec_sendpacket(struct ifnet *ifp,struct mbuf *m) {
struct mcf548x_enet_struct *sc = ifp->if_softc;
@@ -1164,122 +1157,6 @@ static void mcf548x_fec_initialize_hardware(struct mcf548x_enet_struct *sc)
MCF548X_FEC_CTCWR(chan) = MCF548X_FEC_CTCWR_TFCW | MCF548X_FEC_CTCWR_CRC;
}
- /*
- * Initialize PHY(LXT971A):
- *
- * Generally, on power up, the LXT971A reads its configuration
- * pins to check for forced operation, If not cofigured for
- * forced operation, it uses auto-negotiation/parallel detection
- * to automatically determine line operating conditions.
- * If the PHY device on the other side of the link supports
- * auto-negotiation, the LXT971A auto-negotiates with it
- * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not
- * support auto-negotiation, the LXT971A automatically detects
- * the presence of either link pulses(10Mbps PHY) or Idle
- * symbols(100Mbps) and sets its operating conditions accordingly.
- *
- * When auto-negotiation is controlled by software, the following
- * steps are recommended.
- *
- * Note:
- * The physical address is dependent on hardware configuration.
- *
- * Returns: void
- *
- * Notes:
- *
- */
-static void mcf548x_fec_initialize_phy(struct mcf548x_enet_struct *sc)
- {
- int timeout;
- unsigned short phyAddr = 0;
- int chan = sc->chan;
-
- /*
- * Reset PHY, then delay 300ns
- */
- mcf548x_eth_mii_write(sc, phyAddr, 0x0, 0x8000);
-
- rtems_task_wake_after(2);
-
- /* MII100 */
-
- /*
- * Set the auto-negotiation advertisement register bits
- */
- mcf548x_eth_mii_write(sc, phyAddr, 0x4, 0x01e1);
-
- /*
- * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation
- */
- mcf548x_eth_mii_write(sc, phyAddr, 0x0, 0x1200);
-
- /*
- * Wait for AN completion
- */
- timeout = 0x100;
-#if 0
- do
- {
-
- rtems_task_wake_after(2);
-
- if((timeout--) == 0)
- {
-
-#if defined(ETH_DEBUG)
- printf ("MCF548XFEC PHY auto neg failed." "\r\n");
-#endif
-
- }
-
- if(mcf548x_eth_mii_read(sc, phyAddr, 0x1, &phyStatus) != true)
- {
-
-#if defined(ETH_DEBUG)
- printf ("MCF548XFEC PHY auto neg failed: 0x%04x." "\r\n", phyStatus);
-#endif
-
- return;
-
- }
-
- } while((phyStatus & 0x0020) != 0x0020);
-
-#endif
-#if ETH_PROMISCOUS_MODE
- MCF548X_FEC_RCR(chan) |= MCF548X_FEC_RCR_PROM; /* set to promiscous mode */
-#endif
-
-#if ETH_LOOP_MODE
- MCF548X_FEC_RCR(chan) |= MCF548X_FEC_RCR_LOOP; /* set to loopback mode */
-#endif
-
-#if defined(ETH_DEBUG)
- int i;
- unsigned short phyStatus;
- /*
- * Print PHY registers after initialization.
- */
- for(i = 0; i < 9; i++)
- {
-
- mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus);
- printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus);
-
- }
-
- for(i = 16; i < 21; i++)
- {
-
- mcf548x_eth_mii_read(sc, phyAddr, i, &phyStatus);
- printf ("Mii reg %d: 0x%04x" "\r\n", i, phyStatus);
-
- }
-#endif /* ETH_DEBUG */
-
- }
-
/*
* Send packet (caller provides header).
@@ -1312,11 +1189,7 @@ static void mcf548x_fec_startDMA(struct mcf548x_enet_struct *sc)
0, /* the amount to increment the source address per transfer */
(void *)&MCF548X_FEC_FECRFDR(chan), /* the address to move data to */
0, /* the amount to increment the destination address per transfer */
-#if 0
- 4, /* the number of bytes to transfer independent of the transfer size */
-#else
ETHER_MAX_LEN, /* the number of bytes to transfer independent of the transfer size */
-#endif
0, /* the number bytes in of each data movement (1, 2, or 4) */
MCF548X_FEC_RX_INITIATOR(chan), /* what device initiates the DMA */
2, /* priority of the DMA */
@@ -1340,11 +1213,7 @@ static void mcf548x_fec_startDMA(struct mcf548x_enet_struct *sc)
0, /* the amount to increment the source address per transfer */
(void *)&MCF548X_FEC_FECTFDR(chan), /* the address to move data to */
0, /* the amount to increment the destination address per transfer */
-#if 0
- 4, /* the number of bytes to transfer independent of the transfer size */
-#else
ETHER_MAX_LEN, /* the number of bytes to transfer independent of the transfer size */
-#endif
0, /* the number bytes in of each data movement (1, 2, or 4) */
MCF548X_FEC_TX_INITIATOR(chan), /* what device initiates the DMA */
1, /* priority of the DMA */
@@ -1433,10 +1302,6 @@ static void mcf548x_fec_init(void *arg)
* reset and Set up mcf548x FEC hardware
*/
mcf548x_fec_initialize_hardware(sc);
- /*
- * Set up the phy
- */
- mcf548x_fec_initialize_phy(sc);
/*
* Start driver tasks
@@ -1474,6 +1339,10 @@ static void mcf548x_fec_init(void *arg)
MCF548X_FEC_RCR(chan) &= ~MCF548X_FEC_RCR_PROM;
/*
+ * init timer so the "watchdog function gets called periodically
+ */
+ ifp->if_timer = 1;
+ /*
* Tell the world that we're running.
*/
ifp->if_flags |= IFF_RUNNING;
@@ -1543,12 +1412,8 @@ static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc)
* recycle pending tx buffers
* FIXME: try to extract pending Tx buffers
*/
-#if 0
- mcf548x_fec_tx_bd_requeue(sc);
-#else
mcf548x_fec_retire_tbd(sc,true);
#endif
-#endif
/*
* re-initialize the FEC hardware
*/
@@ -1608,6 +1473,11 @@ static int mcf548x_fec_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_
switch(command)
{
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ rtems_mii_ioctl (&(sc->mdio_info),sc,command,(void *)data);
+ break;
+
case SIOCGIFADDR:
case SIOCSIFADDR:
@@ -1685,6 +1555,84 @@ static int mcf548x_fec_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_
/*
+ * init the PHY and adapt FEC settings
+ */
+int mcf548x_fec_mode_adapt(struct ifnet *ifp)
+{
+ int result = 0;
+ struct mcf548x_enet_struct *sc = ifp->if_softc;
+ int media = IFM_MAKEWORD( 0, 0, 0, sc->phy_default);
+ int chan = sc->chan;
+
+ /*
+ * fetch media status
+ */
+ result = mcf548x_fec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media);
+ if (result != 0) {
+ return result;
+ }
+ /*
+ * status is unchanged? then do nothing
+ */
+ if (media == sc->media_state) {
+ return 0;
+ }
+ /*
+ * otherwise: for the first call, try to negotiate mode
+ */
+ if (sc->media_state == 0) {
+ /*
+ * set media status: set auto negotiation -> start auto-negotiation
+ */
+ media = IFM_MAKEWORD(0,IFM_AUTO,0,sc->phy_default);
+ result = mcf548x_fec_ioctl(ifp,SIOCSIFMEDIA,(caddr_t)&media);
+ if (result != 0) {
+ return result;
+ }
+ /*
+ * wait for auto-negotiation to terminate
+ */
+ do {
+ media = IFM_MAKEWORD(0,0,0,sc->phy_default);
+ result = mcf548x_fec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media);
+ if (result != 0) {
+ return result;
+ }
+ } while (IFM_NONE == IFM_SUBTYPE(media));
+ }
+
+ /*
+ * now set HW according to media results:
+ */
+
+ /*
+ * if we are half duplex then switch to half duplex
+ */
+ if (0 == (IFM_FDX & IFM_OPTIONS(media))) {
+ MCF548X_FEC_TCR(chan) &= ~MCF548X_FEC_TCR_FDEN;
+ }
+ else {
+ MCF548X_FEC_TCR(chan) |= MCF548X_FEC_TCR_FDEN;
+ }
+ /*
+ * store current media state for future compares
+ */
+ sc->media_state = media;
+
+ return 0;
+}
+
+/*
+ * periodically poll the PHY. if mode has changed,
+ * then adjust the FEC settings
+ */
+static void mcf548x_fec_watchdog( struct ifnet *ifp)
+{
+ mcf548x_fec_mode_adapt(ifp);
+ ifp->if_timer = FEC_WATCHDOG_TIMEOUT;
+}
+
+/*
* Attach the MCF548X fec driver to the system
*/
int rtems_mcf548x_fec_driver_attach(struct rtems_bsdnet_ifconfig *config)
@@ -1828,6 +1776,20 @@ int rtems_mcf548x_fec_driver_attach(struct rtems_bsdnet_ifconfig *config)
sc->acceptBroadcast = !config->ignore_broadcast;
+ /*
+ * setup info about mdio interface
+ */
+ sc->mdio_info.mdio_r = mcf548x_eth_mii_read;
+ sc->mdio_info.mdio_w = mcf548x_eth_mii_write;
+ sc->mdio_info.has_gmii = 0; /* we do not support gigabit IF */
+
+ /*
+ * XXX: Although most hardware builders will assign the PHY addresses
+ * like this, this should be more configurable
+ */
+ sc->phy_default = unitNumber-1;
+ sc->phy_chan = 0; /* assume all MII accesses are via FEC0 */
+
/*
* Set up network interface values
*/
@@ -1839,6 +1801,7 @@ int rtems_mcf548x_fec_driver_attach(struct rtems_bsdnet_ifconfig *config)
ifp->if_ioctl = mcf548x_fec_ioctl;
ifp->if_start = mcf548x_fec_tx_start;
ifp->if_output = ether_output;
+ ifp->if_watchdog = mcf548x_fec_watchdog; /* XXX: timer is set in "init" */
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
/*ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;*/