if_ffec_mpc8xx: New MDIO driver support

Update #3523.
This commit is contained in:
Sebastian Huber 2018-09-14 09:43:12 +02:00
parent 457b4fc927
commit d101ed8614
2 changed files with 129 additions and 172 deletions

View File

@ -178,6 +178,7 @@ SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
#elif defined(LIBBSP_POWERPC_TQM8XX_BSP_H) #elif defined(LIBBSP_POWERPC_TQM8XX_BSP_H)
RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL); RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL);
SYSINIT_DRIVER_REFERENCE(ukphy, miibus);
#endif #endif

View File

@ -76,10 +76,15 @@
#include <net/if_types.h> #include <net/if_types.h>
#include <net/if_var.h> #include <net/if_var.h>
#include <machine/bus.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <rtems/bsd/local/miibus_if.h>
#include <bsp/irq.h> #include <bsp/irq.h>
#include <rtems/rtems_mii_ioctl.h>
#include <rtems/bsd/bsd.h> #include <rtems/bsd/bsd.h>
#include <errno.h>
/* FIXME */ /* FIXME */
rtems_id rtems_id
@ -142,6 +147,8 @@ struct m8xx_fec_enet_struct {
int txBdTail; int txBdTail;
int txBdActiveCount; int txBdActiveCount;
struct callout watchdogCallout; struct callout watchdogCallout;
device_t miibus;
struct mii_data *mii_softc;
m8xxBufferDescriptor_t *rxBdBase; m8xxBufferDescriptor_t *rxBdBase;
m8xxBufferDescriptor_t *txBdBase; m8xxBufferDescriptor_t *txBdBase;
rtems_id rxDaemonTid; rtems_id rxDaemonTid;
@ -151,9 +158,7 @@ struct m8xx_fec_enet_struct {
/* /*
* MDIO/Phy info * MDIO/Phy info
*/ */
struct rtems_mdio_info mdio_info;
int phy_default; int phy_default;
int media_state; /* (last detected) state of media */
/* /*
* Statistics * Statistics
*/ */
@ -177,8 +182,6 @@ struct m8xx_fec_enet_struct {
unsigned long txRawWait; unsigned long txRawWait;
}; };
int fec_mode_adapt (struct ifnet *ifp);
#define FEC_LOCK(sc) mtx_lock(&(sc)->mtx) #define FEC_LOCK(sc) mtx_lock(&(sc)->mtx)
#define FEC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define FEC_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
@ -201,9 +204,6 @@ static void fec_wait_for_event(struct m8xx_fec_enet_struct *sc,
FEC_LOCK(sc); FEC_LOCK(sc);
} }
/* declare ioctl function for internal use */
static int fec_ioctl (struct ifnet *ifp,
ioctl_command_t command, caddr_t data);
/***************************************************************************\ /***************************************************************************\
| MII Management access functions | | MII Management access functions |
\***************************************************************************/ \***************************************************************************/
@ -231,31 +231,15 @@ static void fec_mdio_init
/* /*
* set clock divider * set clock divider
*/ */
m8xx.fec.mii_speed = BSP_bus_frequency / 25000000 / 2 + 1; m8xx.fec.mii_speed = BSP_bus_frequency / 1250000 / 2 + 1;
} }
/*=========================================================================*\ static int
| Function: | fec_miibus_read_reg(device_t dev, int phy, int reg)
\*-------------------------------------------------------------------------*/
int fec_mdio_read
(
/*-------------------------------------------------------------------------*\
| Purpose: |
| read register of a phy |
+---------------------------------------------------------------------------+
| Input Parameters: |
\*-------------------------------------------------------------------------*/
int phy, /* PHY number to access or -1 */
void *uarg, /* unit argument */
unsigned reg, /* register address */
uint32_t *pval /* ptr to read buffer */
)
/*-------------------------------------------------------------------------*\
| Return Value: |
| 0, if ok, else error |
\*=========================================================================*/
{ {
struct m8xx_fec_enet_struct *sc = uarg;/* control structure */ struct m8xx_fec_enet_struct *sc;
sc = device_get_softc(dev);
/* /*
* make sure we work with a valid phy * make sure we work with a valid phy
@ -270,7 +254,7 @@ int fec_mdio_read
/* /*
* invalid phy number * invalid phy number
*/ */
return EINVAL; return 0;
} }
/* /*
* clear MII transfer event bit * clear MII transfer event bit
@ -283,6 +267,7 @@ int fec_mdio_read
M8xx_FEC_MII_DATA_OP_RD | M8xx_FEC_MII_DATA_OP_RD |
M8xx_FEC_MII_DATA_PHYAD(phy) | M8xx_FEC_MII_DATA_PHYAD(phy) |
M8xx_FEC_MII_DATA_PHYRA(reg) | M8xx_FEC_MII_DATA_PHYRA(reg) |
M8xx_FEC_MII_DATA_TA |
M8xx_FEC_MII_DATA_WDATA(0)); M8xx_FEC_MII_DATA_WDATA(0));
/* /*
@ -295,34 +280,15 @@ int fec_mdio_read
/* /*
* fetch read data, if available * fetch read data, if available
*/ */
if (pval != NULL) { return M8xx_FEC_MII_DATA_RDATA(m8xx.fec.mii_data);
*pval = M8xx_FEC_MII_DATA_RDATA(m8xx.fec.mii_data);
}
return 0;
} }
/*=========================================================================*\ static int
| Function: | fec_miibus_write_reg(device_t dev, int phy, int reg, int val)
\*-------------------------------------------------------------------------*/
int fec_mdio_write
(
/*-------------------------------------------------------------------------*\
| Purpose: |
| write register of a phy |
+---------------------------------------------------------------------------+
| Input Parameters: |
\*-------------------------------------------------------------------------*/
int phy, /* PHY number to access or -1 */
void *uarg, /* unit argument */
unsigned reg, /* register address */
uint32_t val /* write value */
)
/*-------------------------------------------------------------------------*\
| Return Value: |
| 0, if ok, else error |
\*=========================================================================*/
{ {
struct m8xx_fec_enet_struct *sc = uarg;/* control structure */ struct m8xx_fec_enet_struct *sc;
sc = device_get_softc(dev);
/* /*
* make sure we work with a valid phy * make sure we work with a valid phy
@ -350,6 +316,7 @@ int fec_mdio_write
M8xx_FEC_MII_DATA_OP_WR | M8xx_FEC_MII_DATA_OP_WR |
M8xx_FEC_MII_DATA_PHYAD(phy) | M8xx_FEC_MII_DATA_PHYAD(phy) |
M8xx_FEC_MII_DATA_PHYRA(reg) | M8xx_FEC_MII_DATA_PHYRA(reg) |
M8xx_FEC_MII_DATA_TA |
M8xx_FEC_MII_DATA_WDATA(val)); M8xx_FEC_MII_DATA_WDATA(val));
/* /*
@ -516,6 +483,9 @@ m8xx_fec_initialize_hardware (struct m8xx_fec_enet_struct *sc)
sc->txBdHead = sc->txBdTail = 0; sc->txBdHead = sc->txBdTail = 0;
sc->txBdActiveCount = 0; sc->txBdActiveCount = 0;
/* Set pin multiplexing */
m8xx.fec.ecntrl = M8xx_FEC_ECNTRL_FEC_PINMUX;
/* /*
* Mask all FEC interrupts and clear events * Mask all FEC interrupts and clear events
*/ */
@ -879,17 +849,57 @@ static void fec_watchdog (void *arg)
{ {
struct m8xx_fec_enet_struct *sc = arg; struct m8xx_fec_enet_struct *sc = arg;
fec_mode_adapt(sc->ifp); mii_tick(sc->mii_softc);
callout_reset(&sc->watchdogCallout, FEC_WATCHDOG_TIMEOUT * hz, callout_reset(&sc->watchdogCallout, FEC_WATCHDOG_TIMEOUT * hz,
fec_watchdog, sc); fec_watchdog, sc);
} }
static int
fec_media_change(struct ifnet * ifp)
{
struct m8xx_fec_enet_struct *sc;
struct mii_data *mii;
int error;
sc = ifp->if_softc;
mii = sc->mii_softc;
if (mii != NULL) {
FEC_LOCK(sc);
error = mii_mediachg(sc->mii_softc);
FEC_UNLOCK(sc);
} else {
error = ENXIO;
}
return (error);
}
static void
fec_media_status(struct ifnet * ifp, struct ifmediareq *ifmr)
{
struct m8xx_fec_enet_struct *sc;
struct mii_data *mii;
sc = ifp->if_softc;
mii = sc->mii_softc;
if (mii != NULL) {
FEC_LOCK(sc);
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
FEC_UNLOCK(sc);
}
}
static void fec_init (void *arg) static void fec_init (void *arg)
{ {
struct m8xx_fec_enet_struct *sc = arg; struct m8xx_fec_enet_struct *sc = arg;
struct ifnet *ifp = sc->ifp; struct ifnet *ifp = sc->ifp;
if (sc->txDaemonTid == 0) { if (sc->txDaemonTid == 0) {
int error;
/* /*
* Set up FEC hardware * Set up FEC hardware
@ -907,6 +917,13 @@ static void fec_init (void *arg)
sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, fec_txDaemon, sc); sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, fec_txDaemon, sc);
sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, fec_rxDaemon, sc); sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, fec_rxDaemon, sc);
/* Attach the mii driver. */
error = mii_attach(sc->dev, &sc->miibus, ifp, fec_media_change,
fec_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
MII_OFFSET_ANY, 0);
if (error == 0) {
sc->mii_softc = device_get_softc(sc->miibus);
}
} }
/* /*
@ -917,10 +934,13 @@ static void fec_init (void *arg)
else else
m8xx.fec.r_cntrl &= ~M8xx_FEC_R_CNTRL_PROM; m8xx.fec.r_cntrl &= ~M8xx_FEC_R_CNTRL_PROM;
if (sc->mii_softc != NULL ) {
/* /*
* init timer so the "watchdog function gets called periodically * init timer so the "watchdog function gets called periodically
*/ */
mii_mediachg(sc->mii_softc);
callout_reset(&sc->watchdogCallout, hz, fec_watchdog, sc); callout_reset(&sc->watchdogCallout, hz, fec_watchdog, sc);
}
/* /*
* Tell the world that we're running. * Tell the world that we're running.
@ -930,7 +950,7 @@ static void fec_init (void *arg)
/* /*
* Enable receiver and transmitter * Enable receiver and transmitter
*/ */
m8xx.fec.ecntrl = M8xx_FEC_ECNTRL_ETHER_EN | M8xx_FEC_ECNTRL_FEC_PINMUX; m8xx.fec.ecntrl |= M8xx_FEC_ECNTRL_ETHER_EN;
} }
/* /*
@ -954,7 +974,7 @@ static void fec_stop (struct ifnet *ifp)
/* /*
* Shut down receiver and transmitter * Shut down receiver and transmitter
*/ */
m8xx.fec.ecntrl = 0x0; m8xx.fec.ecntrl &= ~M8xx_FEC_ECNTRL_ETHER_EN;
} }
/* /*
@ -982,21 +1002,18 @@ static void fec_enet_stats (struct m8xx_fec_enet_struct *sc)
printf (" Raw output wait:%-8lu\n", sc->txRawWait); printf (" Raw output wait:%-8lu\n", sc->txRawWait);
} }
static int fec_ioctl (struct ifnet *ifp, static int fec_ioctl (struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
ioctl_command_t command, caddr_t data)
{ {
struct m8xx_fec_enet_struct *sc = ifp->if_softc; struct m8xx_fec_enet_struct *sc;
int error = 0; struct ifreq *ifr;
int error;
struct mii_data *mii;
switch (command) { sc = ifp->if_softc;
/* ifr = (struct ifreq *)data;
* access PHY via MII
*/
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
rtems_mii_ioctl (&(sc->mdio_info),sc,command,(void *)data);
break;
error = 0;
switch (cmd) {
case SIOCSIFFLAGS: case SIOCSIFFLAGS:
FEC_LOCK(sc); FEC_LOCK(sc);
if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_UP) {
@ -1011,6 +1028,18 @@ static int fec_ioctl (struct ifnet *ifp,
FEC_UNLOCK(sc); FEC_UNLOCK(sc);
break; break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
mii = sc->mii_softc;
if (mii != NULL) {
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
} else {
error = ether_ioctl(ifp, cmd, data);
}
break;
case SIO_RTEMS_SHOW_STATS: case SIO_RTEMS_SHOW_STATS:
fec_enet_stats (sc); fec_enet_stats (sc);
break; break;
@ -1019,97 +1048,29 @@ static int fec_ioctl (struct ifnet *ifp,
* FIXME: All sorts of multicast commands need to be added here! * FIXME: All sorts of multicast commands need to be added here!
*/ */
default: default:
error = ether_ioctl(ifp, command, data); error = ether_ioctl(ifp, cmd, data);
break; break;
} }
return error; return error;
} }
/*=========================================================================*\ static void
| Function: | fec_miibus_statchg(device_t dev)
\*-------------------------------------------------------------------------*/
int fec_mode_adapt
(
/*-------------------------------------------------------------------------*\
| Purpose: |
| init the PHY and adapt FEC settings |
+---------------------------------------------------------------------------+
| Input Parameters: |
\*-------------------------------------------------------------------------*/
struct ifnet *ifp
)
/*-------------------------------------------------------------------------*\
| Return Value: |
| 0, if success |
\*=========================================================================*/
{ {
int result = 0; struct m8xx_fec_enet_struct *sc;
struct m8xx_fec_enet_struct *sc = ifp->if_softc; struct mii_data *mii;
int media = IFM_MAKEWORD( 0, 0, 0, sc->phy_default);
#ifdef DEBUG sc = device_get_softc(dev);
printf("c"); mii = sc->mii_softc;
#endif
/*
* fetch media status
*/
result = fec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media);
if (result != 0) {
return result;
}
#ifdef DEBUG
printf("C");
#endif
/*
* 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 = 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 = fec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media);
if (result != 0) {
return result;
}
} while (IFM_NONE == IFM_SUBTYPE(media));
}
/* if (mii == NULL ||
* now set HW according to media results: (IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
*/ m8xx.fec.x_cntrl |= M8xx_FEC_X_CNTRL_FDEN;
/* m8xx.fec.r_cntrl &= ~M8xx_FEC_R_CNTRL_DRT;
* if we are half duplex then switch to half duplex } else {
*/
if (0 == (IFM_FDX & IFM_OPTIONS(media))) {
m8xx.fec.x_cntrl &= ~M8xx_FEC_X_CNTRL_FDEN; m8xx.fec.x_cntrl &= ~M8xx_FEC_X_CNTRL_FDEN;
m8xx.fec.r_cntrl |= M8xx_FEC_R_CNTRL_DRT; m8xx.fec.r_cntrl |= M8xx_FEC_R_CNTRL_DRT;
} }
else {
m8xx.fec.x_cntrl |= M8xx_FEC_X_CNTRL_FDEN;
m8xx.fec.r_cntrl &= ~M8xx_FEC_R_CNTRL_DRT;
}
/*
* store current media state for future compares
*/
sc->media_state = media;
return 0;
} }
static int fec_attach (device_t dev) static int fec_attach (device_t dev)
@ -1119,13 +1080,6 @@ static int fec_attach (device_t dev)
int unitNumber = device_get_unit(dev); int unitNumber = device_get_unit(dev);
uint8_t hwaddr[ETHER_ADDR_LEN]; uint8_t hwaddr[ETHER_ADDR_LEN];
/*
* enable FEC functionality at hardware pins*
* PD[3-15] are FEC pins
*/
m8xx.pdpar |= 0x1fff;
m8xx.pddir |= 0x1fff;
rtems_bsd_get_mac_address(device_get_name(dev), device_get_unit(dev), rtems_bsd_get_mac_address(device_get_name(dev), device_get_unit(dev),
hwaddr); hwaddr);
@ -1139,12 +1093,6 @@ static int fec_attach (device_t dev)
sc->rxBdCount = RX_BUF_COUNT; sc->rxBdCount = RX_BUF_COUNT;
sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF;
/*
* setup info about mdio interface
*/
sc->mdio_info.mdio_r = fec_mdio_read;
sc->mdio_info.mdio_w = fec_mdio_write;
sc->mdio_info.has_gmii = 0; /* we do not support gigabit IF */
/* /*
* assume: IF 1 -> PHY 0 * assume: IF 1 -> PHY 0
*/ */
@ -1190,6 +1138,11 @@ static device_method_t fec_methods[] = {
DEVMETHOD(device_probe, fec_probe), DEVMETHOD(device_probe, fec_probe),
DEVMETHOD(device_attach, fec_attach), DEVMETHOD(device_attach, fec_attach),
/* MII Interface */
DEVMETHOD(miibus_readreg, fec_miibus_read_reg),
DEVMETHOD(miibus_writereg, fec_miibus_write_reg),
DEVMETHOD(miibus_statchg, fec_miibus_statchg),
DEVMETHOD_END DEVMETHOD_END
}; };
@ -1200,7 +1153,10 @@ static driver_t fec_nexus_driver = {
}; };
static devclass_t fec_devclass; static devclass_t fec_devclass;
DRIVER_MODULE(fec, nexus, fec_nexus_driver, fec_devclass, 0, 0); DRIVER_MODULE(fec, nexus, fec_nexus_driver, fec_devclass, 0, 0);
DRIVER_MODULE(miibus, fec, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(fec, nexus, 1, 1, 1); MODULE_DEPEND(fec, nexus, 1, 1, 1);
MODULE_DEPEND(fec, ether, 1, 1, 1); MODULE_DEPEND(fec, ether, 1, 1, 1);