880 lines
19 KiB
C
880 lines
19 KiB
C
|
/*-
|
||
|
* Copyright (c) 2011-2012 Semihalf.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
* SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <sys/cdefs.h>
|
||
|
__FBSDID("$FreeBSD$");
|
||
|
|
||
|
#include <sys/param.h>
|
||
|
#include <sys/systm.h>
|
||
|
#include <sys/kernel.h>
|
||
|
#include <sys/module.h>
|
||
|
#include <sys/bus.h>
|
||
|
#include <sys/rman.h>
|
||
|
#include <sys/malloc.h>
|
||
|
#include <sys/mbuf.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/sysctl.h>
|
||
|
#include <sys/sockio.h>
|
||
|
|
||
|
#include <machine/bus.h>
|
||
|
#include <machine/resource.h>
|
||
|
|
||
|
#include <net/ethernet.h>
|
||
|
#include <net/if.h>
|
||
|
#include <net/if_dl.h>
|
||
|
#include <net/if_media.h>
|
||
|
#include <net/if_types.h>
|
||
|
#include <net/if_arp.h>
|
||
|
|
||
|
#include <dev/fdt/fdt_common.h>
|
||
|
#include <dev/mii/mii.h>
|
||
|
#include <dev/mii/miivar.h>
|
||
|
#include <dev/ofw/ofw_bus.h>
|
||
|
#include <dev/ofw/ofw_bus_subr.h>
|
||
|
#include <dev/ofw/openfirm.h>
|
||
|
|
||
|
#include "miibus_if.h"
|
||
|
|
||
|
#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h>
|
||
|
#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h>
|
||
|
#include <contrib/ncsw/inc/xx_ext.h>
|
||
|
|
||
|
#include "fman.h"
|
||
|
#include "if_dtsec.h"
|
||
|
#include "if_dtsec_im.h"
|
||
|
#include "if_dtsec_rm.h"
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group dTSEC private defines.
|
||
|
* @{
|
||
|
*/
|
||
|
/**
|
||
|
* dTSEC FMan MAC exceptions info struct.
|
||
|
*/
|
||
|
struct dtsec_fm_mac_ex_str {
|
||
|
const int num;
|
||
|
const char *str;
|
||
|
};
|
||
|
|
||
|
/* XXX: Handle to FM_MAC instance of dTSEC0 */
|
||
|
/* From QorIQ Data Path Acceleration Architecture Reference Manual, Rev 2, page
|
||
|
* 3-37, "The MII management hardware is shared by all dTSECs... only through
|
||
|
* the MIIM registers of dTSEC1 can external PHY's be accessed and configured."
|
||
|
*/
|
||
|
static t_Handle dtsec_mdio_mac_handle;
|
||
|
/** @} */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group FMan MAC routines.
|
||
|
* @{
|
||
|
*/
|
||
|
#define DTSEC_MAC_EXCEPTIONS_END (-1)
|
||
|
|
||
|
/**
|
||
|
* FMan MAC exceptions.
|
||
|
*/
|
||
|
static const struct dtsec_fm_mac_ex_str dtsec_fm_mac_exceptions[] = {
|
||
|
{ e_FM_MAC_EX_10G_MDIO_SCAN_EVENTMDIO, "MDIO scan event" },
|
||
|
{ e_FM_MAC_EX_10G_MDIO_CMD_CMPL, "MDIO command completion" },
|
||
|
{ e_FM_MAC_EX_10G_REM_FAULT, "Remote fault" },
|
||
|
{ e_FM_MAC_EX_10G_LOC_FAULT, "Local fault" },
|
||
|
{ e_FM_MAC_EX_10G_1TX_ECC_ER, "Transmit frame ECC error" },
|
||
|
{ e_FM_MAC_EX_10G_TX_FIFO_UNFL, "Transmit FIFO underflow" },
|
||
|
{ e_FM_MAC_EX_10G_TX_FIFO_OVFL, "Receive FIFO overflow" },
|
||
|
{ e_FM_MAC_EX_10G_TX_ER, "Transmit frame error" },
|
||
|
{ e_FM_MAC_EX_10G_RX_FIFO_OVFL, "Receive FIFO overflow" },
|
||
|
{ e_FM_MAC_EX_10G_RX_ECC_ER, "Receive frame ECC error" },
|
||
|
{ e_FM_MAC_EX_10G_RX_JAB_FRM, "Receive jabber frame" },
|
||
|
{ e_FM_MAC_EX_10G_RX_OVRSZ_FRM, "Receive oversized frame" },
|
||
|
{ e_FM_MAC_EX_10G_RX_RUNT_FRM, "Receive runt frame" },
|
||
|
{ e_FM_MAC_EX_10G_RX_FRAG_FRM, "Receive fragment frame" },
|
||
|
{ e_FM_MAC_EX_10G_RX_LEN_ER, "Receive payload length error" },
|
||
|
{ e_FM_MAC_EX_10G_RX_CRC_ER, "Receive CRC error" },
|
||
|
{ e_FM_MAC_EX_10G_RX_ALIGN_ER, "Receive alignment error" },
|
||
|
{ e_FM_MAC_EX_1G_BAB_RX, "Babbling receive error" },
|
||
|
{ e_FM_MAC_EX_1G_RX_CTL, "Receive control (pause frame) interrupt" },
|
||
|
{ e_FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET, "Graceful transmit stop "
|
||
|
"complete" },
|
||
|
{ e_FM_MAC_EX_1G_BAB_TX, "Babbling transmit error" },
|
||
|
{ e_FM_MAC_EX_1G_TX_CTL, "Transmit control (pause frame) interrupt" },
|
||
|
{ e_FM_MAC_EX_1G_TX_ERR, "Transmit error" },
|
||
|
{ e_FM_MAC_EX_1G_LATE_COL, "Late collision" },
|
||
|
{ e_FM_MAC_EX_1G_COL_RET_LMT, "Collision retry limit" },
|
||
|
{ e_FM_MAC_EX_1G_TX_FIFO_UNDRN, "Transmit FIFO underrun" },
|
||
|
{ e_FM_MAC_EX_1G_MAG_PCKT, "Magic Packet detected when dTSEC is in "
|
||
|
"Magic Packet detection mode" },
|
||
|
{ e_FM_MAC_EX_1G_MII_MNG_RD_COMPLET, "MII management read completion" },
|
||
|
{ e_FM_MAC_EX_1G_MII_MNG_WR_COMPLET, "MII management write completion" },
|
||
|
{ e_FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET, "Graceful receive stop "
|
||
|
"complete" },
|
||
|
{ e_FM_MAC_EX_1G_TX_DATA_ERR, "Internal data error on transmit" },
|
||
|
{ e_FM_MAC_EX_1G_RX_DATA_ERR, "Internal data error on receive" },
|
||
|
{ e_FM_MAC_EX_1G_1588_TS_RX_ERR, "Time-Stamp Receive Error" },
|
||
|
{ e_FM_MAC_EX_1G_RX_MIB_CNT_OVFL, "MIB counter overflow" },
|
||
|
{ DTSEC_MAC_EXCEPTIONS_END, "" }
|
||
|
};
|
||
|
|
||
|
static const char *
|
||
|
dtsec_fm_mac_ex_to_str(e_FmMacExceptions exception)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; dtsec_fm_mac_exceptions[i].num != exception &&
|
||
|
dtsec_fm_mac_exceptions[i].num != DTSEC_MAC_EXCEPTIONS_END; ++i)
|
||
|
;
|
||
|
|
||
|
if (dtsec_fm_mac_exceptions[i].num == DTSEC_MAC_EXCEPTIONS_END)
|
||
|
return ("<Unknown Exception>");
|
||
|
|
||
|
return (dtsec_fm_mac_exceptions[i].str);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_fm_mac_mdio_event_callback(t_Handle h_App,
|
||
|
e_FmMacExceptions exception)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = h_App;
|
||
|
device_printf(sc->sc_dev, "MDIO event %i: %s.\n", exception,
|
||
|
dtsec_fm_mac_ex_to_str(exception));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_fm_mac_exception_callback(t_Handle app, e_FmMacExceptions exception)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = app;
|
||
|
device_printf(sc->sc_dev, "MAC exception %i: %s.\n", exception,
|
||
|
dtsec_fm_mac_ex_to_str(exception));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_fm_mac_free(struct dtsec_softc *sc)
|
||
|
{
|
||
|
if (sc->sc_mach == NULL)
|
||
|
return;
|
||
|
|
||
|
FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
|
||
|
FM_MAC_Free(sc->sc_mach);
|
||
|
sc->sc_mach = NULL;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
dtsec_fm_mac_init(struct dtsec_softc *sc, uint8_t *mac)
|
||
|
{
|
||
|
t_FmMacParams params;
|
||
|
t_Error error;
|
||
|
|
||
|
memset(¶ms, 0, sizeof(params));
|
||
|
memcpy(¶ms.addr, mac, sizeof(params.addr));
|
||
|
|
||
|
params.baseAddr = sc->sc_fm_base + sc->sc_mac_mem_offset;
|
||
|
params.enetMode = sc->sc_mac_enet_mode;
|
||
|
params.macId = sc->sc_eth_id;
|
||
|
params.mdioIrq = sc->sc_mac_mdio_irq;
|
||
|
params.f_Event = dtsec_fm_mac_mdio_event_callback;
|
||
|
params.f_Exception = dtsec_fm_mac_exception_callback;
|
||
|
params.h_App = sc;
|
||
|
params.h_Fm = sc->sc_fmh;
|
||
|
|
||
|
sc->sc_mach = FM_MAC_Config(¶ms);
|
||
|
if (sc->sc_hidden)
|
||
|
return (0);
|
||
|
if (sc->sc_mach == NULL) {
|
||
|
device_printf(sc->sc_dev, "couldn't configure FM_MAC module.\n"
|
||
|
);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
error = FM_MAC_ConfigResetOnInit(sc->sc_mach, TRUE);
|
||
|
if (error != E_OK) {
|
||
|
device_printf(sc->sc_dev, "couldn't enable reset on init "
|
||
|
"feature.\n");
|
||
|
dtsec_fm_mac_free(sc);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
/* Do not inform about pause frames */
|
||
|
error = FM_MAC_ConfigException(sc->sc_mach, e_FM_MAC_EX_1G_RX_CTL,
|
||
|
FALSE);
|
||
|
if (error != E_OK) {
|
||
|
device_printf(sc->sc_dev, "couldn't disable pause frames "
|
||
|
"exception.\n");
|
||
|
dtsec_fm_mac_free(sc);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
error = FM_MAC_Init(sc->sc_mach);
|
||
|
if (error != E_OK) {
|
||
|
device_printf(sc->sc_dev, "couldn't initialize FM_MAC module."
|
||
|
"\n");
|
||
|
dtsec_fm_mac_free(sc);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
/** @} */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group FMan PORT routines.
|
||
|
* @{
|
||
|
*/
|
||
|
static const char *
|
||
|
dtsec_fm_port_ex_to_str(e_FmPortExceptions exception)
|
||
|
{
|
||
|
|
||
|
switch (exception) {
|
||
|
case e_FM_PORT_EXCEPTION_IM_BUSY:
|
||
|
return ("IM: RX busy");
|
||
|
default:
|
||
|
return ("<Unknown Exception>");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
dtsec_fm_port_rx_exception_callback(t_Handle app,
|
||
|
e_FmPortExceptions exception)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = app;
|
||
|
device_printf(sc->sc_dev, "RX exception: %i: %s.\n", exception,
|
||
|
dtsec_fm_port_ex_to_str(exception));
|
||
|
}
|
||
|
|
||
|
void
|
||
|
dtsec_fm_port_tx_exception_callback(t_Handle app,
|
||
|
e_FmPortExceptions exception)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = app;
|
||
|
device_printf(sc->sc_dev, "TX exception: %i: %s.\n", exception,
|
||
|
dtsec_fm_port_ex_to_str(exception));
|
||
|
}
|
||
|
|
||
|
e_FmPortType
|
||
|
dtsec_fm_port_rx_type(enum eth_dev_type type)
|
||
|
{
|
||
|
switch (type) {
|
||
|
case ETH_DTSEC:
|
||
|
return (e_FM_PORT_TYPE_RX);
|
||
|
case ETH_10GSEC:
|
||
|
return (e_FM_PORT_TYPE_RX_10G);
|
||
|
default:
|
||
|
return (e_FM_PORT_TYPE_DUMMY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
e_FmPortType
|
||
|
dtsec_fm_port_tx_type(enum eth_dev_type type)
|
||
|
{
|
||
|
|
||
|
switch (type) {
|
||
|
case ETH_DTSEC:
|
||
|
return (e_FM_PORT_TYPE_TX);
|
||
|
case ETH_10GSEC:
|
||
|
return (e_FM_PORT_TYPE_TX_10G);
|
||
|
default:
|
||
|
return (e_FM_PORT_TYPE_DUMMY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_fm_port_free_both(struct dtsec_softc *sc)
|
||
|
{
|
||
|
if (sc->sc_rxph) {
|
||
|
FM_PORT_Free(sc->sc_rxph);
|
||
|
sc->sc_rxph = NULL;
|
||
|
}
|
||
|
|
||
|
if (sc->sc_txph) {
|
||
|
FM_PORT_Free(sc->sc_txph);
|
||
|
sc->sc_txph = NULL;
|
||
|
}
|
||
|
}
|
||
|
/** @} */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group IFnet routines.
|
||
|
* @{
|
||
|
*/
|
||
|
static int
|
||
|
dtsec_if_enable_locked(struct dtsec_softc *sc)
|
||
|
{
|
||
|
int error;
|
||
|
|
||
|
DTSEC_LOCK_ASSERT(sc);
|
||
|
|
||
|
error = FM_MAC_Enable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
|
||
|
if (error != E_OK)
|
||
|
return (EIO);
|
||
|
|
||
|
error = FM_PORT_Enable(sc->sc_rxph);
|
||
|
if (error != E_OK)
|
||
|
return (EIO);
|
||
|
|
||
|
error = FM_PORT_Enable(sc->sc_txph);
|
||
|
if (error != E_OK)
|
||
|
return (EIO);
|
||
|
|
||
|
sc->sc_ifnet->if_drv_flags |= IFF_DRV_RUNNING;
|
||
|
|
||
|
/* Refresh link state */
|
||
|
dtsec_miibus_statchg(sc->sc_dev);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
dtsec_if_disable_locked(struct dtsec_softc *sc)
|
||
|
{
|
||
|
int error;
|
||
|
|
||
|
DTSEC_LOCK_ASSERT(sc);
|
||
|
|
||
|
error = FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
|
||
|
if (error != E_OK)
|
||
|
return (EIO);
|
||
|
|
||
|
error = FM_PORT_Disable(sc->sc_rxph);
|
||
|
if (error != E_OK)
|
||
|
return (EIO);
|
||
|
|
||
|
error = FM_PORT_Disable(sc->sc_txph);
|
||
|
if (error != E_OK)
|
||
|
return (EIO);
|
||
|
|
||
|
sc->sc_ifnet->if_drv_flags &= ~IFF_DRV_RUNNING;
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
dtsec_if_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
struct ifreq *ifr;
|
||
|
int error;
|
||
|
|
||
|
sc = ifp->if_softc;
|
||
|
ifr = (struct ifreq *)data;
|
||
|
error = 0;
|
||
|
|
||
|
/* Basic functionality to achieve media status reports */
|
||
|
switch (command) {
|
||
|
case SIOCSIFFLAGS:
|
||
|
DTSEC_LOCK(sc);
|
||
|
|
||
|
if (sc->sc_ifnet->if_flags & IFF_UP)
|
||
|
error = dtsec_if_enable_locked(sc);
|
||
|
else
|
||
|
error = dtsec_if_disable_locked(sc);
|
||
|
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
break;
|
||
|
|
||
|
case SIOCGIFMEDIA:
|
||
|
case SIOCSIFMEDIA:
|
||
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media,
|
||
|
command);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error = ether_ioctl(ifp, command, data);
|
||
|
}
|
||
|
|
||
|
return (error);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_if_tick(void *arg)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = arg;
|
||
|
|
||
|
/* TODO */
|
||
|
DTSEC_LOCK(sc);
|
||
|
|
||
|
mii_tick(sc->sc_mii);
|
||
|
callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc);
|
||
|
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_if_deinit_locked(struct dtsec_softc *sc)
|
||
|
{
|
||
|
|
||
|
DTSEC_LOCK_ASSERT(sc);
|
||
|
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
callout_drain(&sc->sc_tick_callout);
|
||
|
DTSEC_LOCK(sc);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_if_init_locked(struct dtsec_softc *sc)
|
||
|
{
|
||
|
int error;
|
||
|
|
||
|
DTSEC_LOCK_ASSERT(sc);
|
||
|
|
||
|
/* Set MAC address */
|
||
|
error = FM_MAC_ModifyMacAddr(sc->sc_mach,
|
||
|
(t_EnetAddr *)IF_LLADDR(sc->sc_ifnet));
|
||
|
if (error != E_OK) {
|
||
|
device_printf(sc->sc_dev, "couldn't set MAC address.\n");
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
/* Start MII polling */
|
||
|
if (sc->sc_mii)
|
||
|
callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc);
|
||
|
|
||
|
if (sc->sc_ifnet->if_flags & IFF_UP) {
|
||
|
error = dtsec_if_enable_locked(sc);
|
||
|
if (error != 0)
|
||
|
goto err;
|
||
|
} else {
|
||
|
error = dtsec_if_disable_locked(sc);
|
||
|
if (error != 0)
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
err:
|
||
|
dtsec_if_deinit_locked(sc);
|
||
|
device_printf(sc->sc_dev, "initialization error.\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_if_init(void *data)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = data;
|
||
|
|
||
|
DTSEC_LOCK(sc);
|
||
|
dtsec_if_init_locked(sc);
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_if_start(struct ifnet *ifp)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
|
||
|
sc = ifp->if_softc;
|
||
|
DTSEC_LOCK(sc);
|
||
|
sc->sc_start_locked(sc);
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_if_watchdog(struct ifnet *ifp)
|
||
|
{
|
||
|
/* TODO */
|
||
|
}
|
||
|
/** @} */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group IFmedia routines.
|
||
|
* @{
|
||
|
*/
|
||
|
static int
|
||
|
dtsec_ifmedia_upd(struct ifnet *ifp)
|
||
|
{
|
||
|
struct dtsec_softc *sc = ifp->if_softc;
|
||
|
|
||
|
DTSEC_LOCK(sc);
|
||
|
mii_mediachg(sc->sc_mii);
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dtsec_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
||
|
{
|
||
|
struct dtsec_softc *sc = ifp->if_softc;
|
||
|
|
||
|
DTSEC_LOCK(sc);
|
||
|
|
||
|
mii_pollstat(sc->sc_mii);
|
||
|
|
||
|
ifmr->ifm_active = sc->sc_mii->mii_media_active;
|
||
|
ifmr->ifm_status = sc->sc_mii->mii_media_status;
|
||
|
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
}
|
||
|
/** @} */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group dTSEC bus interface.
|
||
|
* @{
|
||
|
*/
|
||
|
static void
|
||
|
dtsec_configure_mode(struct dtsec_softc *sc)
|
||
|
{
|
||
|
char tunable[64];
|
||
|
|
||
|
snprintf(tunable, sizeof(tunable), "%s.independent_mode",
|
||
|
device_get_nameunit(sc->sc_dev));
|
||
|
|
||
|
sc->sc_mode = DTSEC_MODE_REGULAR;
|
||
|
TUNABLE_INT_FETCH(tunable, &sc->sc_mode);
|
||
|
|
||
|
if (sc->sc_mode == DTSEC_MODE_REGULAR) {
|
||
|
sc->sc_port_rx_init = dtsec_rm_fm_port_rx_init;
|
||
|
sc->sc_port_tx_init = dtsec_rm_fm_port_tx_init;
|
||
|
sc->sc_start_locked = dtsec_rm_if_start_locked;
|
||
|
} else {
|
||
|
sc->sc_port_rx_init = dtsec_im_fm_port_rx_init;
|
||
|
sc->sc_port_tx_init = dtsec_im_fm_port_tx_init;
|
||
|
sc->sc_start_locked = dtsec_im_if_start_locked;
|
||
|
}
|
||
|
|
||
|
device_printf(sc->sc_dev, "Configured for %s mode.\n",
|
||
|
(sc->sc_mode == DTSEC_MODE_REGULAR) ? "regular" : "independent");
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dtsec_attach(device_t dev)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
int error;
|
||
|
struct ifnet *ifp;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
|
||
|
sc->sc_dev = dev;
|
||
|
sc->sc_mac_mdio_irq = NO_IRQ;
|
||
|
sc->sc_eth_id = device_get_unit(dev);
|
||
|
|
||
|
|
||
|
/* Check if MallocSmart allocator is ready */
|
||
|
if (XX_MallocSmartInit() != E_OK)
|
||
|
return (ENXIO);
|
||
|
|
||
|
XX_TrackInit();
|
||
|
|
||
|
/* Init locks */
|
||
|
mtx_init(&sc->sc_lock, device_get_nameunit(dev),
|
||
|
"DTSEC Global Lock", MTX_DEF);
|
||
|
|
||
|
mtx_init(&sc->sc_mii_lock, device_get_nameunit(dev),
|
||
|
"DTSEC MII Lock", MTX_DEF);
|
||
|
|
||
|
/* Init callouts */
|
||
|
callout_init(&sc->sc_tick_callout, CALLOUT_MPSAFE);
|
||
|
|
||
|
/* Read configuraton */
|
||
|
if ((error = fman_get_handle(&sc->sc_fmh)) != 0)
|
||
|
return (error);
|
||
|
|
||
|
if ((error = fman_get_muram_handle(&sc->sc_muramh)) != 0)
|
||
|
return (error);
|
||
|
|
||
|
if ((error = fman_get_bushandle(&sc->sc_fm_base)) != 0)
|
||
|
return (error);
|
||
|
|
||
|
/* Configure working mode */
|
||
|
dtsec_configure_mode(sc);
|
||
|
|
||
|
/* If we are working in regular mode configure BMAN and QMAN */
|
||
|
if (sc->sc_mode == DTSEC_MODE_REGULAR) {
|
||
|
/* Create RX buffer pool */
|
||
|
error = dtsec_rm_pool_rx_init(sc);
|
||
|
if (error != 0)
|
||
|
return (EIO);
|
||
|
|
||
|
/* Create RX frame queue range */
|
||
|
error = dtsec_rm_fqr_rx_init(sc);
|
||
|
if (error != 0)
|
||
|
return (EIO);
|
||
|
|
||
|
/* Create frame info pool */
|
||
|
error = dtsec_rm_fi_pool_init(sc);
|
||
|
if (error != 0)
|
||
|
return (EIO);
|
||
|
|
||
|
/* Create TX frame queue range */
|
||
|
error = dtsec_rm_fqr_tx_init(sc);
|
||
|
if (error != 0)
|
||
|
return (EIO);
|
||
|
}
|
||
|
|
||
|
/* Init FMan MAC module. */
|
||
|
error = dtsec_fm_mac_init(sc, sc->sc_mac_addr);
|
||
|
if (error != 0) {
|
||
|
dtsec_detach(dev);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* XXX: All phys are connected to MDIO interface of the first dTSEC
|
||
|
* device (dTSEC0). We have to save handle to the FM_MAC instance of
|
||
|
* dTSEC0, which is used later during phy's registers accesses. Another
|
||
|
* option would be adding new property to DTS pointing to correct dTSEC
|
||
|
* instance, of which FM_MAC handle has to be used for phy's registers
|
||
|
* accesses. We did not want to add new properties to DTS, thus this
|
||
|
* quite ugly hack.
|
||
|
*/
|
||
|
if (sc->sc_eth_id == 0)
|
||
|
dtsec_mdio_mac_handle = sc->sc_mach;
|
||
|
if (sc->sc_hidden)
|
||
|
return (0);
|
||
|
|
||
|
/* Init FMan TX port */
|
||
|
error = sc->sc_port_tx_init(sc, device_get_unit(sc->sc_dev));
|
||
|
if (error != 0) {
|
||
|
dtsec_detach(dev);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
/* Init FMan RX port */
|
||
|
error = sc->sc_port_rx_init(sc, device_get_unit(sc->sc_dev));
|
||
|
if (error != 0) {
|
||
|
dtsec_detach(dev);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
/* Create network interface for upper layers */
|
||
|
ifp = sc->sc_ifnet = if_alloc(IFT_ETHER);
|
||
|
if (ifp == NULL) {
|
||
|
device_printf(sc->sc_dev, "if_alloc() failed.\n");
|
||
|
dtsec_detach(dev);
|
||
|
return (ENOMEM);
|
||
|
}
|
||
|
|
||
|
ifp->if_softc = sc;
|
||
|
ifp->if_mtu = ETHERMTU; /* TODO: Configure */
|
||
|
ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST;
|
||
|
ifp->if_init = dtsec_if_init;
|
||
|
ifp->if_start = dtsec_if_start;
|
||
|
ifp->if_ioctl = dtsec_if_ioctl;
|
||
|
ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
|
||
|
|
||
|
if (sc->sc_phy_addr >= 0)
|
||
|
if_initname(ifp, device_get_name(sc->sc_dev),
|
||
|
device_get_unit(sc->sc_dev));
|
||
|
else
|
||
|
if_initname(ifp, "dtsec_phy", device_get_unit(sc->sc_dev));
|
||
|
|
||
|
/* TODO */
|
||
|
#if 0
|
||
|
IFQ_SET_MAXLEN(&ifp->if_snd, TSEC_TX_NUM_DESC - 1);
|
||
|
ifp->if_snd.ifq_drv_maxlen = TSEC_TX_NUM_DESC - 1;
|
||
|
IFQ_SET_READY(&ifp->if_snd);
|
||
|
#endif
|
||
|
ifp->if_capabilities = 0; /* TODO: Check */
|
||
|
ifp->if_capenable = ifp->if_capabilities;
|
||
|
|
||
|
/* Attach PHY(s) */
|
||
|
error = mii_attach(sc->sc_dev, &sc->sc_mii_dev, ifp, dtsec_ifmedia_upd,
|
||
|
dtsec_ifmedia_sts, BMSR_DEFCAPMASK, sc->sc_phy_addr,
|
||
|
MII_OFFSET_ANY, 0);
|
||
|
if (error) {
|
||
|
device_printf(sc->sc_dev, "attaching PHYs failed: %d\n", error);
|
||
|
dtsec_detach(sc->sc_dev);
|
||
|
return (error);
|
||
|
}
|
||
|
sc->sc_mii = device_get_softc(sc->sc_mii_dev);
|
||
|
|
||
|
/* Attach to stack */
|
||
|
ether_ifattach(ifp, sc->sc_mac_addr);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dtsec_detach(device_t dev)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
if_t ifp;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
ifp = sc->sc_ifnet;
|
||
|
|
||
|
if (device_is_attached(dev)) {
|
||
|
ether_ifdetach(ifp);
|
||
|
/* Shutdown interface */
|
||
|
DTSEC_LOCK(sc);
|
||
|
dtsec_if_deinit_locked(sc);
|
||
|
DTSEC_UNLOCK(sc);
|
||
|
}
|
||
|
|
||
|
if (sc->sc_ifnet) {
|
||
|
if_free(sc->sc_ifnet);
|
||
|
sc->sc_ifnet = NULL;
|
||
|
}
|
||
|
|
||
|
if (sc->sc_mode == DTSEC_MODE_REGULAR) {
|
||
|
/* Free RX/TX FQRs */
|
||
|
dtsec_rm_fqr_rx_free(sc);
|
||
|
dtsec_rm_fqr_tx_free(sc);
|
||
|
|
||
|
/* Free frame info pool */
|
||
|
dtsec_rm_fi_pool_free(sc);
|
||
|
|
||
|
/* Free RX buffer pool */
|
||
|
dtsec_rm_pool_rx_free(sc);
|
||
|
}
|
||
|
|
||
|
dtsec_fm_mac_free(sc);
|
||
|
dtsec_fm_port_free_both(sc);
|
||
|
|
||
|
/* Destroy lock */
|
||
|
mtx_destroy(&sc->sc_lock);
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dtsec_suspend(device_t dev)
|
||
|
{
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dtsec_resume(device_t dev)
|
||
|
{
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dtsec_shutdown(device_t dev)
|
||
|
{
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
/** @} */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group MII bus interface.
|
||
|
* @{
|
||
|
*/
|
||
|
int
|
||
|
dtsec_miibus_readreg(device_t dev, int phy, int reg)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
uint16_t data;
|
||
|
t_Error error;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
|
||
|
if (phy != sc->sc_phy_addr)
|
||
|
return (0xFFFF);
|
||
|
|
||
|
DTSEC_MII_LOCK(sc);
|
||
|
error = FM_MAC_MII_ReadPhyReg(dtsec_mdio_mac_handle, phy, reg, &data);
|
||
|
DTSEC_MII_UNLOCK(sc);
|
||
|
if (error != E_OK) {
|
||
|
device_printf(dev, "Error while reading from PHY (NetCommSw "
|
||
|
"error: %d)\n", error);
|
||
|
return (0xFFFF);
|
||
|
}
|
||
|
|
||
|
return ((int)data);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dtsec_miibus_writereg(device_t dev, int phy, int reg, int value)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
t_Error error;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
|
||
|
if (phy != sc->sc_phy_addr)
|
||
|
return (EINVAL);
|
||
|
|
||
|
DTSEC_MII_LOCK(sc);
|
||
|
error = FM_MAC_MII_WritePhyReg(dtsec_mdio_mac_handle, phy, reg, value);
|
||
|
DTSEC_MII_UNLOCK(sc);
|
||
|
if (error != E_OK) {
|
||
|
device_printf(dev, "Error while writing to PHY (NetCommSw "
|
||
|
"error: %d).\n", error);
|
||
|
return (EIO);
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
dtsec_miibus_statchg(device_t dev)
|
||
|
{
|
||
|
struct dtsec_softc *sc;
|
||
|
e_EnetSpeed speed;
|
||
|
bool duplex;
|
||
|
int error;
|
||
|
|
||
|
sc = device_get_softc(dev);
|
||
|
|
||
|
DTSEC_LOCK_ASSERT(sc);
|
||
|
|
||
|
duplex = ((sc->sc_mii->mii_media_active & IFM_GMASK) == IFM_FDX);
|
||
|
|
||
|
switch (IFM_SUBTYPE(sc->sc_mii->mii_media_active)) {
|
||
|
case IFM_1000_T:
|
||
|
case IFM_1000_SX:
|
||
|
speed = e_ENET_SPEED_1000;
|
||
|
break;
|
||
|
|
||
|
case IFM_100_TX:
|
||
|
speed = e_ENET_SPEED_100;
|
||
|
break;
|
||
|
|
||
|
case IFM_10_T:
|
||
|
speed = e_ENET_SPEED_10;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
speed = e_ENET_SPEED_10;
|
||
|
}
|
||
|
|
||
|
error = FM_MAC_AdjustLink(sc->sc_mach, speed, duplex);
|
||
|
if (error != E_OK)
|
||
|
device_printf(sc->sc_dev, "error while adjusting MAC speed.\n");
|
||
|
}
|
||
|
/** @} */
|