/*-
* Copyright (c) 2006 Sam Leffler, Errno Consulting
* 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.
*/
/*
* Register manipulation macros that expect bit field defines
* to follow the convention that an _S suffix is appended for
* a shift count, while the field mask has no suffix.
*/
#define SM(_v, _f) (((_v) << _f##_S) & _f)
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
/*
* On AP30/AR5312 the switch is configured in one of two ways:
* as a ROUTER or as a BRIDGE. The ROUTER config sets up ports
* 0-3 as LAN ports, port 4 as the WAN port, and port 5 connects
* to the MAC in the 5312. The BRIDGE config sets up ports
* 0-4 as LAN ports with port 5 connected to the MAC in the 5312.
*/
struct mvPhyConfig {
uint16_t switchPortAddr;/* switch port associated with PHY */
uint16_t vlanSetting; /* VLAN table setting for PHY */
uint32_t portControl; /* switch port control setting for PHY */
};
static const struct mvPhyConfig dumbConfig[] = {
{ 0x18, 0x2e, /* PHY port 0 = LAN port 0 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x19, 0x2d, /* PHY port 1 = LAN port 1 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1a, 0x2b, /* PHY port 2 = LAN port 2 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1b, 0x27, /* PHY port 3 = LAN port 3 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1c, 0x25, /* PHY port 4 = LAN port 4 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1d, 0x1f, /* PHY port 5 = CPU port */
MV_PORT_CONTROL_PORT_STATE_FORWARDING }
};
#if 0 /* XXX what are these? */
static const struct mvPhyConfig routerConfig[] = {
{ 0x18, 0x2e, /* PHY port 0 = LAN port 0 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x19, 0x2d, /* PHY port 1 = LAN port 1 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1a, 0x2b, /* PHY port 2 = LAN port 2 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1b, 0x27, /* PHY port 3 = LAN port 3 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1c, 0x1020, /* PHY port 4 = WAN port */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
/* NB: 0x0f =>'s send only to LAN ports */
{ 0x1d, 0x0f, /* PHY port 5 = CPU port */
MV_PORT_CONTROL_PORT_STATE_FORWARDING
#if 0
| MV_PORT_CONTROL_INGRESS_TRAILER
| MV_PORT_CONTROL_EGRESS_MODE
#endif
}
};
static const struct mvPhyConfig bridgeConfig[] = {
{ 0x18, 0x3e, /* PHY port 0 = LAN port 0 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x19, 0x3d, /* PHY port 1 = LAN port 1 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1a, 0x3b, /* PHY port 2 = LAN port 2 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1b, 0x37, /* PHY port 3 = LAN port 3 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
{ 0x1c, 0x37, /* PHY port 4 = LAN port 4 */
MV_PORT_CONTROL_PORT_STATE_FORWARDING },
/* NB: 0x1f =>'s send to all ports */
{ 0x1d, 0x1f, /* PHY port 5 = CPU port */
MV_PORT_CONTROL_PORT_STATE_FORWARDING
#if 0
| MV_PORT_CONTROL_INGRESS_TRAILER
| MV_PORT_CONTROL_EGRESS_MODE
#endif
}
};
#endif
if (MV_PORT(sc) == 0) { /* NB: only when attaching first PHY */
/*
* Set the global switch settings and configure the
* CPU port since it does not probe as a visible PHY.
*/
MV_WRITE(sc, MII_MV_SWITCH_GLOBAL_ADDR, MV_ATU_CONTROL,
SM(MV_ATUCTRL_AGE_TIME_DEFAULT, MV_ATUCTRL_AGE_TIME)
| SM(MV_ATUCTRL_ATU_SIZE_DEFAULT, MV_ATUCTRL_ATU_SIZE));
mvphy_switchconfig(sc, MV_CPU_PORT);
}
PHY_RESET(sc);
static int
mvphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
{
struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
KASSERT(mii_locked(mii));
switch (cmd) {
case MII_POLLSTAT:
/* If we're not polling our PHY instance, just return. */
if (IFM_INST(ife->ifm_media) != sc->mii_inst)
return 0;
break;
case MII_MEDIACHG:
/*
* If the media indicates a different PHY instance,
* isolate ourselves.
*/
if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
/* XXX? */
return 0;
}
/* If the interface is not up, don't do anything. */
if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
break;
mii_phy_setmedia(sc);
break;
case MII_TICK:
/* If we're not currently selected, just return. */
if (IFM_INST(ife->ifm_media) != sc->mii_inst)
return 0;
if (mii_phy_tick(sc) == EJUSTRETURN)
return 0;
break;
case MII_DOWN:
mii_phy_down(sc);
return 0;
}
/* Update the media status. */
mii_phy_status(sc);
MV_WRITE(sc, conf->switchPortAddr, MV_PORT_BASED_VLAN_MAP,
conf->vlanSetting);
/* XXX administrative control of port enable? */
MV_WRITE(sc, conf->switchPortAddr, MV_PORT_CONTROL, conf->portControl);
MV_WRITE(sc, conf->switchPortAddr, MV_PORT_ASSOCIATION_VECTOR,
1 << port);
}
/*
* Flush the Address Translation Unit (ATU).
*/
static void
mvphy_flushatu(struct mii_softc *sc)
{
int status;
uint16_t reg;
int i;
/* wait for any previous request to complete */
/* XXX if busy defer to tick */
/* XXX timeout */
for (i = 0; i < 1000; i++) {
status = MV_READ(sc, MII_MV_SWITCH_GLOBAL_ADDR,
MV_ATU_OPERATION, ®);
if (MV_ATU_IS_BUSY(status))
break;
}
if (i != 1000) {
MV_WRITE(sc, MII_MV_SWITCH_GLOBAL_ADDR, MV_ATU_OPERATION,
MV_ATU_OP_FLUSH_ALL | MV_ATU_BUSY);
} /*else
aprint_error_dev(sc->mii_dev, "timeout waiting for ATU flush\n");*/
}