Poll PHY status using internal e6000sw registers

e6000sw family automatically reflects PHY status in each port's registers.
Therefore it is not necessary to do a full PHY polling squence, which
results in much quicker operation and much less significant usage of
the SMI bus.

Care must be taken that the resulting ifmedia_active is identical to
what the PHY will compute, or gratuitous link status changes will
occur whenever the PHYs update function is called.

This patch implements above improvement. On the occasion set a pointer to
the proc structure to be part of software context instead of being
a global variable.

Submitted by: Marcin Wojtas <mw@semihalf.com>
Obtained from: Semihalf
Sponsored by: Stormshield
Reviewed by: loos
Differential revision: https://reviews.freebsd.org/D10714
This commit is contained in:
Wojciech Macek 2017-05-19 08:24:23 +00:00
parent bf319173f2
commit 2dd020069e
2 changed files with 53 additions and 6 deletions

View File

@ -89,7 +89,7 @@ typedef struct e6000sw_softc {
char *ifname[E6000SW_MAX_PORTS];
device_t miibus[E6000SW_MAX_PORTS];
struct mii_data *mii[E6000SW_MAX_PORTS];
struct callout tick_callout;
struct proc *kproc;
uint32_t cpuports_mask;
uint32_t fixed_mask;
@ -149,8 +149,6 @@ static __inline int e6000sw_is_phyport(e6000sw_softc_t *sc, int port);
static __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *sc,
unsigned int phy);
static struct proc *e6000sw_kproc;
static device_method_t e6000sw_methods[] = {
/* device interface */
DEVMETHOD(device_identify, e6000sw_identify),
@ -419,8 +417,7 @@ e6000sw_attach(device_t dev)
bus_generic_probe(dev);
bus_generic_attach(dev);
kproc_create(e6000sw_tick, sc, &e6000sw_kproc, 0, 0,
"e6000sw tick kproc");
kproc_create(e6000sw_tick, sc, &sc->kproc, 0, 0, "e6000sw tick kproc");
return (0);
@ -1010,23 +1007,65 @@ e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid)
return (0);
}
/*
* Convert port status to ifmedia.
*/
static void
e6000sw_update_ifmedia(uint16_t portstatus, u_int *media_status, u_int *media_active)
{
*media_active = IFM_ETHER;
*media_status = IFM_AVALID;
if ((portstatus & PORT_STATUS_LINK_MASK) != 0)
*media_status |= IFM_ACTIVE;
else {
*media_active |= IFM_NONE;
return;
}
switch (portstatus & PORT_STATUS_SPEED_MASK) {
case PORT_STATUS_SPEED_10:
*media_active |= IFM_10_T;
break;
case PORT_STATUS_SPEED_100:
*media_active |= IFM_100_TX;
break;
case PORT_STATUS_SPEED_1000:
*media_active |= IFM_1000_T;
break;
}
if ((portstatus & PORT_STATUS_DUPLEX_MASK) == 0)
*media_active |= IFM_FDX;
else
*media_active |= IFM_HDX;
}
static void
e6000sw_tick (void *arg)
{
e6000sw_softc_t *sc;
struct mii_softc *miisc;
uint16_t portstatus;
int port;
sc = arg;
E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
for (;;) {
E6000SW_LOCK(sc);
for (port = 0; port < sc->num_ports; port++) {
/* Tick only on PHY ports */
if (!e6000sw_is_phyport(sc, port))
continue;
mii_tick(sc->mii[port]);
portstatus = e6000sw_readreg(sc, REG_PORT(port), PORT_STATUS);
e6000sw_update_ifmedia(portstatus,
&sc->mii[port]->mii_media_status,
&sc->mii[port]->mii_media_active);
LIST_FOREACH(miisc, &sc->mii[port]->mii_phys, mii_list) {
if (IFM_INST(sc->mii[port]->mii_media.ifm_cur->ifm_media)
!= miisc->mii_inst)

View File

@ -55,6 +55,14 @@ struct atu_opt {
* Per-Port Switch Registers
*/
#define PORT_STATUS 0x0
#define PORT_STATUS_SPEED_MASK 0x300
#define PORT_STATUS_SPEED_10 0
#define PORT_STATUS_SPEED_100 1
#define PORT_STATUS_SPEED_1000 2
#define PORT_STATUS_DUPLEX_MASK (1 << 10)
#define PORT_STATUS_LINK_MASK (1 << 11)
#define PORT_STATUS_PHY_DETECT_MASK (1 << 12)
#define PSC_CONTROL 0x1
#define SWITCH_ID 0x3
#define PORT_CONTROL 0x4