summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch.c39
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8327.c77
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_phy.c45
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_phy.h7
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_reg.c129
-rw-r--r--sys/dev/etherswitch/arswitch/arswitchvar.h6
6 files changed, 237 insertions, 66 deletions
diff --git a/sys/dev/etherswitch/arswitch/arswitch.c b/sys/dev/etherswitch/arswitch/arswitch.c
index 129a77d..32509da 100644
--- a/sys/dev/etherswitch/arswitch/arswitch.c
+++ b/sys/dev/etherswitch/arswitch/arswitch.c
@@ -149,8 +149,10 @@ done:
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),
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8327.c b/sys/dev/etherswitch/arswitch/arswitch_8327.c
index 642827d..0bf1265 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_8327.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_8327.c
@@ -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));
/*
+ * 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.
+ * 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;
diff --git a/sys/dev/etherswitch/arswitch/arswitch_phy.c b/sys/dev/etherswitch/arswitch/arswitch_phy.c
index 928ca03..43abaa1 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_phy.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_phy.c
@@ -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 @@ fail:
}
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;
diff --git a/sys/dev/etherswitch/arswitch/arswitch_phy.h b/sys/dev/etherswitch/arswitch/arswitch_phy.h
index a3b7627..885be27 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_phy.h
+++ b/sys/dev/etherswitch/arswitch/arswitch_phy.h
@@ -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__ */
diff --git a/sys/dev/etherswitch/arswitch/arswitch_reg.c b/sys/dev/etherswitch/arswitch/arswitch_reg.c
index 3251da8..d6797a8 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_reg.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_reg.c
@@ -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,93 +134,123 @@ 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;
}
-int
-arswitch_readreg_lsb(device_t dev, int addr)
+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;
- return (arswitch_readreg16(dev, addr));
+ 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_msb(device_t dev, int addr)
+arswitch_readreg(device_t dev, int addr)
{
+ uint16_t phy, reg;
- return (arswitch_readreg16(dev, addr + 2) << 16);
+ arswitch_split_setpage(dev, addr, &phy, &reg);
+ return arswitch_reg_read32(dev, 0x10 | phy, reg);
}
int
-arswitch_writereg_lsb(device_t dev, int addr, int data)
+arswitch_writereg(device_t dev, int addr, int value)
{
+ struct arswitch_softc *sc;
+ uint16_t phy, reg;
- return (arswitch_writereg16(dev, addr, data & 0xffff));
+ 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_writereg_msb(device_t dev, int addr, int data)
+arswitch_readreg_lsb(device_t dev, int addr)
{
- return (arswitch_writereg16(dev, addr + 2, (data >> 16) & 0xffff));
+ return (arswitch_readreg16(dev, addr));
}
int
-arswitch_readreg(device_t dev, int addr)
+arswitch_readreg_msb(device_t dev, int addr)
{
- return (arswitch_readreg_lsb(dev, addr) |
- arswitch_readreg_msb(dev, addr));
+ return (arswitch_readreg16(dev, addr + 2) << 16);
}
int
-arswitch_writereg(device_t dev, int addr, int value)
+arswitch_writereg_lsb(device_t dev, int addr, int data)
{
- struct arswitch_softc *sc;
- int r;
- sc = device_get_softc(dev);
+ return (arswitch_writereg16(dev, addr, data & 0xffff));
+}
- /* 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);
- }
+int
+arswitch_writereg_msb(device_t dev, int addr, int data)
+{
- return r;
+ return (arswitch_writereg16(dev, addr + 2, (data >> 16) & 0xffff));
}
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;
diff --git a/sys/dev/etherswitch/arswitch/arswitchvar.h b/sys/dev/etherswitch/arswitch/arswitchvar.h
index e533e87..51bda96 100644
--- a/sys/dev/etherswitch/arswitch/arswitchvar.h
+++ b/sys/dev/etherswitch/arswitch/arswitchvar.h
@@ -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 {
OpenPOWER on IntegriCloud