Fix up support for the AR8327.

* Even though I got the registers around "right", it seems
  I'm not tickling the MDIO access correctly for the internal PHY
  bus.  Some of the switches are fine poking at the external PHY
  registers; others aren't.  So, enable direct PHY bus access
  for the AR8327, and leave the existing code in place for the
  others.

* Go and shuffle the register access around.  Whilst here,
  restore the 2ms delay if changing page.

* Comment out some of the stub printf()s; there's some upcoming
  work to add port VLAN support.

Tested:

* AP135 development board
* Carambola2 - AR9331 SoC
This commit is contained in:
Adrian Chadd 2015-03-08 03:53:36 +00:00
parent d3df8026cf
commit 78549b94cd
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=279767
6 changed files with 241 additions and 70 deletions

View File

@ -149,8 +149,10 @@ arswitch_probe(device_t dev)
DPRINTF(dev, "chipname=%s, id=%08x\n", chipname, id);
if (chipname != NULL) {
snprintf(desc, sizeof(desc),
"Atheros %s Ethernet Switch",
chipname);
"Atheros %s Ethernet Switch (ver %d rev %d)",
chipname,
sc->chip_ver,
sc->chip_rev);
device_set_desc_copy(dev, desc);
return (BUS_PROBE_DEFAULT);
}
@ -177,9 +179,11 @@ arswitch_attach_phys(struct arswitch_softc *sc)
err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
arswitch_ifmedia_upd, arswitch_ifmedia_sts, \
BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
#if 0
DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
device_get_nameunit(sc->miibus[phy]),
sc->ifp[phy]->if_xname);
#endif
if (err != 0) {
device_printf(sc->sc_dev,
"attaching PHY %d failed\n",
@ -304,6 +308,8 @@ arswitch_attach(device_t dev)
sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
sc->hal.arswitch_atu_flush = ar8xxx_atu_flush;
sc->hal.arswitch_phy_read = arswitch_readphy_internal;
sc->hal.arswitch_phy_write = arswitch_writephy_internal;
/*
* Attach switch related functions
@ -320,8 +326,10 @@ arswitch_attach(device_t dev)
ar8316_attach(sc);
else if (AR8X16_IS_SWITCH(sc, AR8327))
ar8327_attach(sc);
else
else {
DPRINTF(dev, "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype);
return (ENXIO);
}
/* Common defaults. */
sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
@ -348,14 +356,18 @@ arswitch_attach(device_t dev)
sc->numphys = AR8X16_NUM_PHYS;
/* Reset the switch. */
if (arswitch_reset(dev))
if (arswitch_reset(dev)) {
DPRINTF(dev, "%s: arswitch_reset: failed\n", __func__);
return (ENXIO);
}
err = sc->hal.arswitch_hw_setup(sc);
DPRINTF(dev, "%s: hw_setup: err=%d\n", __func__, err);
if (err != 0)
return (err);
err = sc->hal.arswitch_hw_global_setup(sc);
DPRINTF(dev, "%s: hw_global_setup: err=%d\n", __func__, err);
if (err != 0)
return (err);
@ -368,17 +380,20 @@ arswitch_attach(device_t dev)
* Attach the PHYs and complete the bus enumeration.
*/
err = arswitch_attach_phys(sc);
DPRINTF(dev, "%s: attach_phys: err=%d\n", __func__, err);
if (err != 0)
return (err);
/* Default to ingress filters off. */
err = arswitch_set_vlan_mode(sc, 0);
DPRINTF(dev, "%s: set_vlan_mode: err=%d\n", __func__, err);
if (err != 0)
return (err);
bus_generic_probe(dev);
bus_enumerate_hinted_children(dev);
err = bus_generic_attach(dev);
DPRINTF(dev, "%s: bus_generic_attach: err=%d\n", __func__, err);
if (err != 0)
return (err);
@ -803,6 +818,22 @@ arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
return (sc->hal.arswitch_vlan_setvgroup(sc, e));
}
static int
arswitch_readphy(device_t dev, int phy, int reg)
{
struct arswitch_softc *sc = device_get_softc(dev);
return (sc->hal.arswitch_phy_read(dev, phy, reg));
}
static int
arswitch_writephy(device_t dev, int phy, int reg, int val)
{
struct arswitch_softc *sc = device_get_softc(dev);
return (sc->hal.arswitch_phy_write(dev, phy, reg, val));
}
static device_method_t arswitch_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, arswitch_probe),

View File

@ -57,6 +57,9 @@
#include <dev/etherswitch/arswitch/arswitchreg.h>
#include <dev/etherswitch/arswitch/arswitchvar.h>
#include <dev/etherswitch/arswitch/arswitch_reg.h>
#include <dev/etherswitch/arswitch/arswitch_phy.h>
#include <dev/etherswitch/arswitch/arswitch_vlans.h>
#include <dev/etherswitch/arswitch/arswitch_8327.h>
#include "mdio_if.h"
@ -290,7 +293,7 @@ ar8327_fetch_pdata_port(struct arswitch_softc *sc,
sbuf, &val) == 0)
pcfg->rxpause = val;
#if 0
#if 1
device_printf(sc->sc_dev,
"%s: port %d: speed=%d, duplex=%d, txpause=%d, rxpause=%d\n",
__func__,
@ -562,6 +565,7 @@ ar8327_init_pdata(struct arswitch_softc *sc)
/* SGMII config */
bzero(&scfg, sizeof(scfg));
if (ar8327_fetch_pdata_sgmii(sc, &scfg)) {
device_printf(sc->sc_dev, "%s: SGMII cfg?\n", __func__);
t = scfg.sgmii_ctrl;
if (sc->chip_rev == 1)
t |= AR8327_SGMII_CTRL_EN_PLL |
@ -657,12 +661,16 @@ ar8327_hw_global_setup(struct arswitch_softc *sc)
}
/*
* Port setup.
* Port setup. Called at attach time.
*/
static void
ar8327_port_init(struct arswitch_softc *sc, int port)
{
uint32_t t;
int ports;
/* For now, port can see all other ports */
ports = 0x7f;
if (port == AR8X16_PORT_CPU)
t = sc->ar8327.port0_status;
@ -696,7 +704,7 @@ ar8327_port_init(struct arswitch_softc *sc, int port)
t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
/* So this allows traffic to any port except ourselves */
t |= (0x7f & ~(1 << port));
t |= (ports & ~(1 << port));
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t);
}
@ -705,16 +713,18 @@ ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
{
/* XXX stub for now */
device_printf(sc->sc_dev, "%s: called\n", __func__);
// device_printf(sc->sc_dev, "%s: called\n", __func__);
return (0);
}
/*
* Get the port VLAN configuration.
*/
static int
ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
{
/* XXX stub for now */
device_printf(sc->sc_dev, "%s: called\n", __func__);
// device_printf(sc->sc_dev, "%s: called\n", __func__);
return (0);
}
@ -723,6 +733,13 @@ ar8327_reset_vlans(struct arswitch_softc *sc)
{
int i;
uint32_t mode, t;
int ports;
ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
ARSWITCH_LOCK(sc);
/* Clear the existing VLAN configuration */
memset(sc->vid, 0, sizeof(sc->vid));
/*
* Disable mirroring.
@ -732,10 +749,21 @@ ar8327_reset_vlans(struct arswitch_softc *sc)
(0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
/*
* For now, let's default to one portgroup, just so traffic
* flows. All ports can see other ports.
* XXX TODO: disable any Q-in-Q port configuration,
* tagging, egress filters, etc.
*/
/*
* For now, let's default to one portgroup, just so traffic
* flows. All ports can see other ports. There are two CPU GMACs
* (GMAC0, GMAC6), GMAC1..GMAC5 are external PHYs.
*
* (ETHERSWITCH_VLAN_PORT)
*/
ports = 0x7f;
for (i = 0; i < AR8327_NUM_PORTS; i++) {
/* set pvid = 1; there's only one vlangroup */
t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S;
t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S;
@ -749,7 +777,7 @@ ar8327_reset_vlans(struct arswitch_softc *sc)
arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t);
/* Ports can see other ports */
t = (0x7f & ~(1 << i)); /* all ports besides us */
t = (ports & ~(1 << i)); /* all ports besides us */
t |= AR8327_PORT_LOOKUP_LEARN;
/* in_port_only, forward */
@ -769,12 +797,20 @@ ar8327_reset_vlans(struct arswitch_softc *sc)
AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
0);
}
ARSWITCH_UNLOCK(sc);
}
static int
ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
{
device_printf(sc->sc_dev, "%s: called\n", __func__);
#if 0
/* XXX for now, no dot1q vlans */
if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
return (EINVAL);
return (ar8xxx_getvgroup(sc, vg));
#endif
return (0);
}
@ -782,7 +818,12 @@ static int
ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
{
device_printf(sc->sc_dev, "%s: called\n", __func__);
#if 0
/* XXX for now, no dot1q vlans */
if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
return (EINVAL);
return (ar8xxx_setvgroup(sc, vg));
#endif
return (0);
}
@ -832,17 +873,29 @@ ar8327_attach(struct arswitch_softc *sc)
sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup;
sc->hal.arswitch_port_init = ar8327_port_init;
sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup;
sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup;
sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup;
sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get;
sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans;
sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup;
sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup;
sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid;
sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid;
sc->hal.arswitch_atu_flush = ar8327_atu_flush;
/*
* Reading the PHY via the MDIO interface currently doesn't
* work correctly.
*
* So for now, just go direct to the PHY registers themselves.
* This has always worked on external devices, but not internal
* devices (AR934x, AR724x, AR933x.)
*/
sc->hal.arswitch_phy_read = arswitch_readphy_external;
sc->hal.arswitch_phy_write = arswitch_writephy_external;
/* Set the switch vlan capabilities. */
sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q |
ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG;

View File

@ -67,11 +67,46 @@ static SYSCTL_NODE(_debug, OID_AUTO, arswitch, CTLFLAG_RD, 0, "arswitch");
#endif
/*
* access PHYs integrated into the switch chip through the switch's MDIO
* Access PHYs integrated into the switch by going direct
* to the PHY space itself, rather than through the switch
* MDIO register.
*/
int
arswitch_readphy_external(device_t dev, int phy, int reg)
{
int ret;
struct arswitch_softc *sc;
sc = device_get_softc(dev);
ARSWITCH_LOCK(sc);
ret = (MDIO_READREG(device_get_parent(dev), phy, reg));
ARSWITCH_UNLOCK(sc);
return (ret);
}
int
arswitch_writephy_external(device_t dev, int phy, int reg, int data)
{
struct arswitch_softc *sc;
sc = device_get_softc(dev);
ARSWITCH_LOCK(sc);
(void) MDIO_WRITEREG(device_get_parent(dev), phy,
reg, data);
ARSWITCH_UNLOCK(sc);
return (0);
}
/*
* Access PHYs integrated into the switch chip through the switch's MDIO
* control register.
*/
int
arswitch_readphy(device_t dev, int phy, int reg)
arswitch_readphy_internal(device_t dev, int phy, int reg)
{
struct arswitch_softc *sc;
uint32_t data = 0, ctrl;
@ -105,8 +140,10 @@ arswitch_readphy(device_t dev, int phy, int reg)
if ((ctrl & AR8X16_MDIO_CTRL_BUSY) == 0)
break;
}
if (timeout < 0)
if (timeout < 0) {
DPRINTF(dev, "arswitch_readphy(): phy=%d.%02x; timeout=%d\n", phy, reg, timeout);
goto fail;
}
data = arswitch_readreg_lsb(dev, a) &
AR8X16_MDIO_CTRL_DATA_MASK;
ARSWITCH_UNLOCK(sc);
@ -118,7 +155,7 @@ arswitch_readphy(device_t dev, int phy, int reg)
}
int
arswitch_writephy(device_t dev, int phy, int reg, int data)
arswitch_writephy_internal(device_t dev, int phy, int reg, int data)
{
struct arswitch_softc *sc;
uint32_t ctrl;

View File

@ -28,7 +28,10 @@
#ifndef __ARSWITCH_PHY_H__
#define __ARSWITCH_PHY_H__
extern int arswitch_readphy(device_t dev, int phy, int reg);
extern int arswitch_writephy(device_t dev, int phy, int reg, int data);
extern int arswitch_readphy_external(device_t dev, int phy, int reg);
extern int arswitch_writephy_external(device_t dev, int phy, int reg, int data);
extern int arswitch_readphy_internal(device_t dev, int phy, int reg);
extern int arswitch_writephy_internal(device_t dev, int phy, int reg, int data);
#endif /* __ARSWITCH_PHY_H__ */

View File

@ -68,12 +68,13 @@ arswitch_split_setpage(device_t dev, uint32_t addr, uint16_t *phy,
struct arswitch_softc *sc = device_get_softc(dev);
uint16_t page;
page = ((addr) >> 9) & 0xffff;
*phy = (((addr) >> 6) & 0x07) | 0x10;
*reg = ((addr) >> 1) & 0x1f;
page = (addr >> 9) & 0x1ff;
*phy = (addr >> 6) & 0x7;
*reg = (addr >> 1) & 0x1f;
if (sc->page != page) {
MDIO_WRITEREG(device_get_parent(dev), 0x18, 0, page);
DELAY(2000);
sc->page = page;
}
}
@ -87,9 +88,21 @@ static inline int
arswitch_readreg16(device_t dev, int addr)
{
uint16_t phy, reg;
arswitch_split_setpage(dev, addr, &phy, &reg);
return (MDIO_READREG(device_get_parent(dev), phy, reg));
return (MDIO_READREG(device_get_parent(dev), 0x10 | phy, reg));
}
/*
* Write half a register. See above!
*/
static inline int
arswitch_writereg16(device_t dev, int addr, int data)
{
uint16_t phy, reg;
arswitch_split_setpage(dev, addr, &phy, &reg);
return (MDIO_WRITEREG(device_get_parent(dev), 0x10 | phy, reg, data));
}
/*
@ -121,18 +134,70 @@ arswitch_writemmd(device_t dev, int phy, uint16_t dbg_addr,
MII_ATH_MMD_DATA, dbg_data);
}
/*
* Write half a register
*/
static inline int
arswitch_writereg16(device_t dev, int addr, int data)
static uint32_t
arswitch_reg_read32(device_t dev, int phy, int reg)
{
uint16_t phy, reg;
arswitch_split_setpage(dev, addr, &phy, &reg);
return (MDIO_WRITEREG(device_get_parent(dev), phy, reg, data));
uint16_t lo, hi;
lo = MDIO_READREG(device_get_parent(dev), phy, reg);
hi = MDIO_READREG(device_get_parent(dev), phy, reg + 1);
return (hi << 16) | lo;
}
static int
arswitch_reg_write32(device_t dev, int phy, int reg, uint32_t value)
{
struct arswitch_softc *sc;
int r;
uint16_t lo, hi;
sc = device_get_softc(dev);
lo = value & 0xffff;
hi = (uint16_t) (value >> 16);
if (sc->mii_lo_first) {
r = MDIO_WRITEREG(device_get_parent(dev),
phy, reg, lo);
r |= MDIO_WRITEREG(device_get_parent(dev),
phy, reg + 1, hi);
} else {
r = MDIO_WRITEREG(device_get_parent(dev),
phy, reg + 1, hi);
r |= MDIO_WRITEREG(device_get_parent(dev),
phy, reg, lo);
}
return r;
}
int
arswitch_readreg(device_t dev, int addr)
{
uint16_t phy, reg;
arswitch_split_setpage(dev, addr, &phy, &reg);
return arswitch_reg_read32(dev, 0x10 | phy, reg);
}
int
arswitch_writereg(device_t dev, int addr, int value)
{
struct arswitch_softc *sc;
uint16_t phy, reg;
sc = device_get_softc(dev);
arswitch_split_setpage(dev, addr, &phy, &reg);
return (arswitch_reg_write32(dev, 0x10 | phy, reg, value));
}
/*
* Read/write 16 bit values in the switch register space.
*
* Some of the registers are control registers (eg the MDIO
* data versus control space) and so need to be treated
* differently.
*/
int
arswitch_readreg_lsb(device_t dev, int addr)
{
@ -161,53 +226,31 @@ arswitch_writereg_msb(device_t dev, int addr, int data)
return (arswitch_writereg16(dev, addr + 2, (data >> 16) & 0xffff));
}
int
arswitch_readreg(device_t dev, int addr)
{
return (arswitch_readreg_lsb(dev, addr) |
arswitch_readreg_msb(dev, addr));
}
int
arswitch_writereg(device_t dev, int addr, int value)
{
struct arswitch_softc *sc;
int r;
sc = device_get_softc(dev);
/* XXX Check the first write too? */
if (sc->mii_lo_first) {
r = arswitch_writereg_lsb(dev, addr, value);
r |= arswitch_writereg_msb(dev, addr, value);
} else {
r = arswitch_writereg_msb(dev, addr, value);
r |= arswitch_writereg_lsb(dev, addr, value);
}
return r;
}
int
arswitch_modifyreg(device_t dev, int addr, int mask, int set)
{
int value;
value = arswitch_readreg(dev, addr);
uint16_t phy, reg;
arswitch_split_setpage(dev, addr, &phy, &reg);
value = arswitch_reg_read32(dev, 0x10 | phy, reg);
value &= ~mask;
value |= set;
return (arswitch_writereg(dev, addr, value));
return (arswitch_reg_write32(dev, 0x10 | phy, reg, value));
}
int
arswitch_waitreg(device_t dev, int addr, int mask, int val, int timeout)
{
int err, v;
uint16_t phy, reg;
arswitch_split_setpage(dev, addr, &phy, &reg);
err = -1;
while (1) {
v = arswitch_readreg(dev, addr);
v = arswitch_reg_read32(dev, 0x10 | phy, reg);
v &= mask;
if (v == val) {
err = 0;

View File

@ -60,7 +60,7 @@ struct arswitch_softc {
int is_internal_switch;
int chip_ver;
int chip_rev;
int mii_lo_first;
int mii_lo_first; /* Send low data DWORD before high */
ar8x16_switch_type sc_switchtype;
/* should be the max of both pre-AR8327 and AR8327 ports */
char *ifname[ARSWITCH_NUM_PHYS];
@ -98,6 +98,10 @@ struct arswitch_softc {
int *);
int (* arswitch_vlan_set_pvid) (struct arswitch_softc *, int,
int);
/* PHY functions */
int (* arswitch_phy_read) (device_t, int, int);
int (* arswitch_phy_write) (device_t, int, int, int);
} hal;
struct {