From 303bd062b084943267b4499e4122a360cbb0a48b Mon Sep 17 00:00:00 2001 From: Luiz Souza Date: Fri, 9 Mar 2018 03:23:29 -0300 Subject: Enable the SERDES interfaces on MV88E6190 during the switch initialization. (cherry picked from commit 465e5577e8a32d4f98def4ec75a00c09feb140a1) --- sys/dev/etherswitch/e6000sw/e6000sw.c | 111 +++++++++++++++++++++++++++++++ sys/dev/etherswitch/e6000sw/e6000swreg.h | 16 +++++ 2 files changed, 127 insertions(+) diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c index 4d711b6..3ec9abd 100644 --- a/sys/dev/etherswitch/e6000sw/e6000sw.c +++ b/sys/dev/etherswitch/e6000sw/e6000sw.c @@ -118,6 +118,8 @@ static void e6000sw_identify(driver_t *, device_t); static int e6000sw_probe(device_t); static int e6000sw_attach(device_t); static int e6000sw_detach(device_t); +static int e6000sw_read_xmdio(device_t, int, int, int); +static int e6000sw_write_xmdio(device_t, int, int, int, int); static int e6000sw_readphy(device_t, int, int); static int e6000sw_writephy(device_t, int, int, int); static etherswitch_info_t* e6000sw_getinfo(device_t); @@ -602,9 +604,36 @@ e6000sw_attach_miibus(e6000sw_softc_t *sc, int port) return (0); } +static void +e6000sw_serdes_power(device_t dev, int port, bool sgmii) +{ + uint32_t reg; + + /* SGMII */ + reg = e6000sw_read_xmdio(dev, port, E6000SW_SERDES_DEV, + E6000SW_SERDES_SGMII_CTL); + if (sgmii) + reg &= ~E6000SW_SERDES_PDOWN; + else + reg |= E6000SW_SERDES_PDOWN; + e6000sw_write_xmdio(dev, port, E6000SW_SERDES_DEV, + E6000SW_SERDES_SGMII_CTL, reg); + + /* 10GBASE-R/10GBASE-X4/X2 */ + reg = e6000sw_read_xmdio(dev, port, E6000SW_SERDES_DEV, + E6000SW_SERDES_PCS_CTL1); + if (sgmii) + reg |= E6000SW_SERDES_PDOWN; + else + reg &= ~E6000SW_SERDES_PDOWN; + e6000sw_write_xmdio(dev, port, E6000SW_SERDES_DEV, + E6000SW_SERDES_PCS_CTL1, reg); +} + static int e6000sw_attach(device_t dev) { + bool sgmii; e6000sw_softc_t *sc; #ifdef FDT phandle_t child; @@ -685,6 +714,15 @@ e6000sw_attach(device_t dev) reg |= PSC_CONTROL_FORCED_EEE; e6000sw_writereg(sc, REG_PORT(sc, port), PSC_CONTROL, reg); + /* Power on the SERDES interfaces. */ + if (MVSWITCH(sc, MV88E6190) && + (port == 9 || port == 10)) { + if (e6000sw_is_fixed25port(sc, port)) + sgmii = false; + else + sgmii = true; + e6000sw_serdes_power(sc->dev, port, sgmii); + } } /* Don't attach miibus at CPU/fixed ports */ @@ -723,6 +761,79 @@ out_fail: return (err); } +/* XMDIO/Clause 45 access. */ +static int +e6000sw_read_xmdio(device_t dev, int phy, int devaddr, int devreg) +{ + e6000sw_softc_t *sc; + uint32_t reg; + + sc = device_get_softc(dev); + E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { + device_printf(dev, "Timeout while waiting for switch\n"); + return (ETIMEDOUT); + } + + reg = devaddr & SMI_CMD_REG_ADDR_MASK; + reg |= (phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK; + + /* Load C45 register address. */ + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, devreg); + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, + reg | SMI_CMD_OP_C45_ADDR); + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { + device_printf(dev, "Timeout while waiting for switch\n"); + return (ETIMEDOUT); + } + + /* Start C45 read operation. */ + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, + reg | SMI_CMD_OP_C45_READ); + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { + device_printf(dev, "Timeout while waiting for switch\n"); + return (ETIMEDOUT); + } + + /* Read C45 data. */ + reg = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG); + + return (reg & PHY_DATA_MASK); +} + +static int +e6000sw_write_xmdio(device_t dev, int phy, int devaddr, int devreg, int val) +{ + e6000sw_softc_t *sc; + uint32_t reg; + + sc = device_get_softc(dev); + E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { + device_printf(dev, "Timeout while waiting for switch\n"); + return (ETIMEDOUT); + } + + reg = devaddr & SMI_CMD_REG_ADDR_MASK; + reg |= (phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK; + + /* Load C45 register address. */ + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, devreg); + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, + reg | SMI_CMD_OP_C45_ADDR); + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { + device_printf(dev, "Timeout while waiting for switch\n"); + return (ETIMEDOUT); + } + + /* Load data and start the C45 write operation. */ + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, devreg); + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, + reg | SMI_CMD_OP_C45_WRITE); + + return (0); +} + /* * PHY registers are paged. Put page index in reg 22 (accessible from every * page), then access specific register. diff --git a/sys/dev/etherswitch/e6000sw/e6000swreg.h b/sys/dev/etherswitch/e6000sw/e6000swreg.h index 72c6da2..74b73c1 100644 --- a/sys/dev/etherswitch/e6000sw/e6000swreg.h +++ b/sys/dev/etherswitch/e6000sw/e6000swreg.h @@ -247,6 +247,16 @@ struct atu_opt { (SMI_CMD_C22_WRITE | SMI_CMD_BUSY | SMI_CMD_MODE_C22) #define SMI_CMD_OP_C22_READ \ (SMI_CMD_C22_READ | SMI_CMD_BUSY | SMI_CMD_MODE_C22) +#define SMI_CMD_C45 (0 << 12) +#define SMI_CMD_C45_ADDR (0 << 10) +#define SMI_CMD_C45_WRITE (1 << 10) +#define SMI_CMD_C45_READ (3 << 10) +#define SMI_CMD_OP_C45_ADDR \ + (SMI_CMD_C45_ADDR | SMI_CMD_BUSY | SMI_CMD_C45) +#define SMI_CMD_OP_C45_WRITE \ + (SMI_CMD_C45_WRITE | SMI_CMD_BUSY | SMI_CMD_C45) +#define SMI_CMD_OP_C45_READ \ + (SMI_CMD_C45_READ | SMI_CMD_BUSY | SMI_CMD_C45) #define SMI_CMD_DEV_ADDR 5 #define SMI_CMD_DEV_ADDR_MASK 0x3e0 #define SMI_CMD_REG_ADDR_MASK 0x1f @@ -264,6 +274,12 @@ struct atu_opt { #define SCR_AND_MISC_PTR_CFG 0x7000 #define SCR_AND_MISC_DATA_CFG_MASK 0xf0 +/* SERDES registers. */ +#define E6000SW_SERDES_DEV 4 +#define E6000SW_SERDES_PCS_CTL1 0x1000 +#define E6000SW_SERDES_SGMII_CTL 0x2000 +#define E6000SW_SERDES_PDOWN (1 << 11) + #define E6000SW_NUM_VLANS 128 #define E6000SW_NUM_LAGS 16 #define E6000SW_NUM_PHY_REGS 29 -- cgit v1.1