summaryrefslogtreecommitdiff
path: root/sys/arch/octeon
diff options
context:
space:
mode:
authorVisa Hankala <visa@cvs.openbsd.org>2016-06-09 15:29:23 +0000
committerVisa Hankala <visa@cvs.openbsd.org>2016-06-09 15:29:23 +0000
commit0e6a2ccfcf7ddc5948178cb6dbcf2d18be30fca1 (patch)
tree06c42d80aaac057d9e6775a19e4827ca524cbfa2 /sys/arch/octeon
parent3a20ef833fcbb18f25755cb426341afe018a86b4 (diff)
Add SGMII support and PHY addresses for 8-port EdgeRouters. This makes
plain RJ45 ports eth[4-7] usable. ok dlg@, tested by martijn@
Diffstat (limited to 'sys/arch/octeon')
-rw-r--r--sys/arch/octeon/dev/cn30xxgmx.c207
-rw-r--r--sys/arch/octeon/dev/cn30xxgmxreg.h68
-rw-r--r--sys/arch/octeon/dev/cn30xxgmxvar.h10
-rw-r--r--sys/arch/octeon/dev/cn30xxpip.c5
4 files changed, 275 insertions, 15 deletions
diff --git a/sys/arch/octeon/dev/cn30xxgmx.c b/sys/arch/octeon/dev/cn30xxgmx.c
index d2ca8f80b3a..b10f383b41c 100644
--- a/sys/arch/octeon/dev/cn30xxgmx.c
+++ b/sys/arch/octeon/dev/cn30xxgmx.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cn30xxgmx.c,v 1.22 2016/05/29 11:10:25 visa Exp $ */
+/* $OpenBSD: cn30xxgmx.c,v 1.23 2016/06/09 15:29:22 visa Exp $ */
/*
* Copyright (c) 2007 Internet Initiative Japan, Inc.
@@ -75,6 +75,13 @@
#define _GMX_PORT_WR8(sc, off, v) \
bus_space_write_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_regh, (off), (v))
+#define PCS_READ_8(sc, reg) \
+ bus_space_read_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_pcs_regh, \
+ (reg))
+#define PCS_WRITE_8(sc, reg, val) \
+ bus_space_write_8((sc)->sc_port_gmx->sc_regt, (sc)->sc_port_pcs_regh, \
+ (reg), (val))
+
struct cn30xxgmx_port_ops {
int (*port_ops_enable)(struct cn30xxgmx_port_softc *, int);
int (*port_ops_speed)(struct cn30xxgmx_port_softc *);
@@ -95,6 +102,9 @@ int cn30xxgmx_rgmii_speed_newlink(struct cn30xxgmx_port_softc *,
uint64_t *);
int cn30xxgmx_rgmii_speed_speed(struct cn30xxgmx_port_softc *);
int cn30xxgmx_rgmii_timing(struct cn30xxgmx_port_softc *);
+int cn30xxgmx_sgmii_enable(struct cn30xxgmx_port_softc *, int);
+int cn30xxgmx_sgmii_speed(struct cn30xxgmx_port_softc *);
+int cn30xxgmx_sgmii_timing(struct cn30xxgmx_port_softc *);
int cn30xxgmx_tx_ovr_bp_enable(struct cn30xxgmx_port_softc *, int);
int cn30xxgmx_rx_pause_enable(struct cn30xxgmx_port_softc *, int);
@@ -127,6 +137,12 @@ struct cn30xxgmx_port_ops cn30xxgmx_port_ops_rgmii = {
.port_ops_timing = cn30xxgmx_rgmii_timing,
};
+struct cn30xxgmx_port_ops cn30xxgmx_port_ops_sgmii = {
+ .port_ops_enable = cn30xxgmx_sgmii_enable,
+ .port_ops_speed = cn30xxgmx_sgmii_speed,
+ .port_ops_timing = cn30xxgmx_sgmii_timing,
+};
+
struct cn30xxgmx_port_ops cn30xxgmx_port_ops_spi42 = {
/* XXX not implemented */
};
@@ -135,13 +151,14 @@ struct cn30xxgmx_port_ops *cn30xxgmx_port_ops[] = {
[GMX_MII_PORT] = &cn30xxgmx_port_ops_mii,
[GMX_GMII_PORT] = &cn30xxgmx_port_ops_gmii,
[GMX_RGMII_PORT] = &cn30xxgmx_port_ops_rgmii,
+ [GMX_SGMII_PORT] = &cn30xxgmx_port_ops_sgmii,
[GMX_SPI42_PORT] = &cn30xxgmx_port_ops_spi42
};
#ifdef OCTEON_ETH_DEBUG
void *cn30xxgmx_intr_drop_ih;
-struct cn30xxgmx_port_softc *__cn30xxgmx_port_softc[3/* XXX */];
+struct cn30xxgmx_port_softc *__cn30xxgmx_port_softc[GMX_PORT_NUNITS];
#endif
struct cfattach cn30xxgmx_ca = {sizeof(struct cn30xxgmx_softc),
@@ -176,6 +193,14 @@ cn30xxgmx_port_phy_addr(int port)
return -1;
return 7 - port;
+ case BOARD_TYPE_UBIQUITI_E200:
+ if (port >= 0 && port < 4)
+ /* XXX RJ45/SFP combos use the second MDIO. */
+ return port + 4; /* GMX0: eth[4-7] */
+ else if (port >= 16 && port < 20)
+ return port - 16; /* GMX1: eth[0-3] */
+ return -1;
+
default:
if (port >= nitems(octeon_eth_phy_table))
return -1;
@@ -198,7 +223,7 @@ cn30xxgmx_attach(struct device *parent, struct device *self, void *aux)
sc->sc_regt = aa->aa_bust; /* XXX why there are iot? */
status = bus_space_map(sc->sc_regt, aa->aa_addr,
- GMX0_BASE_IF_SIZE, 0, &sc->sc_regh);
+ GMX0_BASE_IF_SIZE(sc->sc_nports), 0, &sc->sc_regh);
if (status != 0)
panic(": can't map register");
@@ -230,6 +255,12 @@ cn30xxgmx_attach(struct device *parent, struct device *self, void *aux)
cn30xxasx_init(&asx_aa, &port_sc->sc_port_asx);
break;
}
+ case GMX_SGMII_PORT:
+ if (bus_space_map(sc->sc_regt,
+ PCS_BASE(sc->sc_unitno, i), PCS_SIZE, 0,
+ &port_sc->sc_port_pcs_regh))
+ panic("could not map PCS registers");
+ break;
default:
/* nothing */
break;
@@ -271,7 +302,8 @@ cn30xxgmx_print(void *aux, const char *pnp)
static const char *types[] = {
[GMX_MII_PORT] = "MII",
[GMX_GMII_PORT] = "GMII",
- [GMX_RGMII_PORT] = "RGMII"
+ [GMX_RGMII_PORT] = "RGMII",
+ [GMX_SGMII_PORT] = "SGMII"
};
#if DEBUG
@@ -297,7 +329,7 @@ cn30xxgmx_init(struct cn30xxgmx_softc *sc)
{
int result = 0;
uint64_t inf_mode;
- int id;
+ int i, id;
inf_mode = bus_space_read_8(sc->sc_regt, sc->sc_regh, GMX0_INF_MODE);
if ((inf_mode & INF_MODE_EN) == 0) {
@@ -360,6 +392,28 @@ cn30xxgmx_init(struct cn30xxgmx_softc *sc)
if (sc->sc_nports == 3)
sc->sc_nports = 2;
break;
+ case OCTEON_MODEL_FAMILY_CN61XX: {
+ uint64_t qlm_cfg;
+
+ if (sc->sc_unitno == 0)
+ qlm_cfg = octeon_xkphys_read_8(MIO_QLM_CFG(2));
+ else
+ qlm_cfg = octeon_xkphys_read_8(MIO_QLM_CFG(0));
+ if ((qlm_cfg & MIO_QLM_CFG_CFG) == 2) {
+ sc->sc_nports = 4;
+ for (i = 0; i < sc->sc_nports; i++)
+ sc->sc_port_types[i] = GMX_SGMII_PORT;
+ } else if ((qlm_cfg & MIO_QLM_CFG_CFG) == 3) {
+ printf("XAUI interface is not supported\n");
+ sc->sc_nports = 0;
+ result = 1;
+ } else {
+ /* The interface is disabled. */
+ sc->sc_nports = 0;
+ result = 1;
+ }
+ break;
+ }
case OCTEON_MODEL_FAMILY_CN38XX:
case OCTEON_MODEL_FAMILY_CN56XX:
case OCTEON_MODEL_FAMILY_CN58XX:
@@ -1103,6 +1157,149 @@ cn30xxgmx_rgmii_timing(struct cn30xxgmx_port_softc *sc)
return 0;
}
+int
+cn30xxgmx_sgmii_enable(struct cn30xxgmx_port_softc *sc, int enable)
+{
+ uint64_t ctl_reg, status, timer_count;
+ uint64_t cpu_freq = octeon_boot_info->eclock / 1000000;
+ int done;
+ int i;
+
+ if (!enable)
+ return 0;
+
+ /* Set link timer interval to 1.6ms. */
+ timer_count = PCS_READ_8(sc, PCS_LINK_TIMER_COUNT);
+ CLR(timer_count, PCS_LINK_TIMER_COUNT_MASK);
+ SET(timer_count, ((1600 * cpu_freq) >> 10) & PCS_LINK_TIMER_COUNT_MASK);
+ PCS_WRITE_8(sc, PCS_LINK_TIMER_COUNT, timer_count);
+
+ /* Reset the PCS. */
+ ctl_reg = PCS_READ_8(sc, PCS_MR_CONTROL);
+ SET(ctl_reg, PCS_MR_CONTROL_RESET);
+ PCS_WRITE_8(sc, PCS_MR_CONTROL, ctl_reg);
+
+ /* Wait for the reset to complete. */
+ done = 0;
+ for (i = 0; i < 1000000; i++) {
+ ctl_reg = PCS_READ_8(sc, PCS_MR_CONTROL);
+ if (!ISSET(ctl_reg, PCS_MR_CONTROL_RESET)) {
+ done = 1;
+ break;
+ }
+ }
+ if (!done) {
+ printf("SGMII reset timeout on port %d\n", sc->sc_port_no);
+ return 1;
+ }
+
+ /* Start a new SGMII autonegotiation. */
+ SET(ctl_reg, PCS_MR_CONTROL_AN_EN);
+ SET(ctl_reg, PCS_MR_CONTROL_RST_AN);
+ CLR(ctl_reg, PCS_MR_CONTROL_PWR_DN);
+ PCS_WRITE_8(sc, PCS_MR_CONTROL, ctl_reg);
+
+ /* Wait for the SGMII autonegotiation to complete. */
+ done = 0;
+ for (i = 0; i < 1000000; i++) {
+ status = PCS_READ_8(sc, PCS_MR_STATUS);
+ if (ISSET(status, PCS_MR_STATUS_AN_CPT)) {
+ done = 1;
+ break;
+ }
+ }
+ if (!done) {
+ printf("SGMII autonegotiation timeout on port %d\n",
+ sc->sc_port_no);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cn30xxgmx_sgmii_speed(struct cn30xxgmx_port_softc *sc)
+{
+ uint64_t misc_ctl, prt_cfg;
+ int tx_burst, tx_slot;
+
+ cn30xxgmx_link_enable(sc, 0);
+
+ prt_cfg = _GMX_PORT_RD8(sc, GMX0_PRT0_CFG);
+
+ if (ISSET(sc->sc_port_mii->mii_media_active, IFM_FDX))
+ SET(prt_cfg, PRTN_CFG_DUPLEX);
+ else
+ CLR(prt_cfg, PRTN_CFG_DUPLEX);
+
+ misc_ctl = PCS_READ_8(sc, PCS_MISC_CTL);
+ CLR(misc_ctl, PCS_MISC_CTL_SAMP_PT);
+
+ /* Disable the GMX port if the link is down. */
+ if (cn30xxgmx_link_status(sc))
+ CLR(misc_ctl, PCS_MISC_CTL_GMXENO);
+ else
+ SET(misc_ctl, PCS_MISC_CTL_GMXENO);
+
+ switch (sc->sc_port_ac->ac_if.if_baudrate) {
+ case IF_Mbps(10):
+ tx_slot = 0x40;
+ tx_burst = 0;
+ CLR(prt_cfg, PRTN_CFG_SPEED);
+ SET(prt_cfg, PRTN_CFG_SPEED_MSB);
+ CLR(prt_cfg, PRTN_CFG_SLOTTIME);
+ misc_ctl |= 25 & PCS_MISC_CTL_SAMP_PT;
+ break;
+ case IF_Mbps(100):
+ tx_slot = 0x40;
+ tx_burst = 0;
+ CLR(prt_cfg, PRTN_CFG_SPEED);
+ CLR(prt_cfg, PRTN_CFG_SPEED_MSB);
+ CLR(prt_cfg, PRTN_CFG_SLOTTIME);
+ misc_ctl |= 5 & PCS_MISC_CTL_SAMP_PT;
+ break;
+ case IF_Gbps(1):
+ tx_slot = 0x200;
+ tx_burst = 0x2000;
+ SET(prt_cfg, PRTN_CFG_SPEED);
+ CLR(prt_cfg, PRTN_CFG_SPEED_MSB);
+ SET(prt_cfg, PRTN_CFG_SLOTTIME);
+ misc_ctl |= 1 & PCS_MISC_CTL_SAMP_PT;
+ break;
+ default:
+ break;
+ }
+
+ PCS_WRITE_8(sc, PCS_MISC_CTL, misc_ctl);
+
+ _GMX_PORT_WR8(sc, GMX0_TX0_SLOT, tx_slot);
+ _GMX_PORT_WR8(sc, GMX0_TX0_BURST, tx_burst);
+ _GMX_PORT_WR8(sc, GMX0_PRT0_CFG, prt_cfg);
+
+ cn30xxgmx_link_enable(sc, 1);
+
+ return 0;
+}
+
+int
+cn30xxgmx_sgmii_timing(struct cn30xxgmx_port_softc *sc)
+{
+ uint64_t rx_frm_ctl;
+
+ cn30xxgmx_tx_thresh(sc, 32);
+
+ rx_frm_ctl =
+ RXN_FRM_CTL_PRE_FREE |
+ RXN_FRM_CTL_CTL_SMAC |
+ RXN_FRM_CTL_CTL_MCST |
+ RXN_FRM_CTL_CTL_DRP |
+ RXN_FRM_CTL_PRE_STRP |
+ RXN_FRM_CTL_PRE_CHK;
+ cn30xxgmx_rx_frm_ctl_enable(sc, rx_frm_ctl);
+
+ return 0;
+}
+
void
cn30xxgmx_stats(struct cn30xxgmx_port_softc *sc)
{
diff --git a/sys/arch/octeon/dev/cn30xxgmxreg.h b/sys/arch/octeon/dev/cn30xxgmxreg.h
index a6c826e9242..d77a3733f54 100644
--- a/sys/arch/octeon/dev/cn30xxgmxreg.h
+++ b/sys/arch/octeon/dev/cn30xxgmxreg.h
@@ -3,7 +3,7 @@
* DONT EDIT THIS FILE
*/
-/* $OpenBSD: cn30xxgmxreg.h,v 1.2 2014/08/11 18:29:56 miod Exp $ */
+/* $OpenBSD: cn30xxgmxreg.h,v 1.3 2016/06/09 15:29:22 visa Exp $ */
/*
* Copyright (c) 2007 Internet Initiative Japan, Inc.
@@ -176,7 +176,9 @@
/* GMX Port Configuration Registers */
-#define PRTN_CFG_XXX_63_4 0xfffffffffffffff0ULL
+#define PRTN_CFG_XXX_63_9 0xfffffffffffffe00ULL
+#define PRTN_CFG_SPEED_MSB 0x0000000000000100ULL
+#define PRTN_CFG_XXX_7_4 0x00000000000000f0ULL
#define PRTN_CFG_SLOTTIME 0x0000000000000008ULL
#define PRTN_CFG_DUPLEX 0x0000000000000004ULL
#define PRTN_CFG_SPEED 0x0000000000000002ULL
@@ -626,18 +628,76 @@
#define INF_MODE_EN 0x0000000000000002ULL
#define INF_MODE_TYPE 0x0000000000000001ULL
+#define MIO_QLM_CFG(x) (0x0001180000001590ULL + (x)*8)
+
+#define MIO_QLM_CFG_CFG 0x000000000000000fULL
+
/* -------------------------------------------------------------------------- */
/* for bus_space(9) */
#define GMX_IF_NUNITS 1
-#define GMX_PORT_NUNITS 3
+#define GMX_PORT_NUNITS 4
#define GMX0_BASE_PORT0 0x0001180008000000ULL
#define GMX0_BASE_PORT1 0x0001180008000800ULL
#define GMX0_BASE_PORT2 0x0001180008001000ULL
#define GMX0_BASE_PORT_SIZE 0x00800
#define GMX0_BASE_IF0 0x0001180008000000ULL
-#define GMX0_BASE_IF_SIZE (GMX0_BASE_PORT_SIZE * GMX_PORT_NUNITS)
+#define GMX0_BASE_IF_SIZE(n) (GMX0_BASE_PORT_SIZE * (n))
+
+/* -------------------------------------------------------------------------- */
+
+/* Low-level SGMII link control */
+
+#define PCS_BASE(g, i) (0x00011800b0001000ULL + 0x20000 * (g) + 0x400 * (i))
+#define PCS_SIZE 0x98
+
+#define PCS_MR_CONTROL 0x00
+#define PCS_MR_STATUS 0x08
+#define PCS_LINK_TIMER_COUNT 0x40
+#define PCS_MISC_CTL 0x78
+
+#define PCS_MR_CONTROL_RES_16_63 0xffffffffffff0000ULL
+#define PCS_MR_CONTROL_RESET 0x0000000000008000ULL
+#define PCS_MR_CONTROL_LOOPBCK1 0x0000000000004000ULL
+#define PCS_MR_CONTROL_SPDLSB 0x0000000000002000ULL
+#define PCS_MR_CONTROL_AN_EN 0x0000000000001000ULL
+#define PCS_MR_CONTROL_PWR_DN 0x0000000000000800ULL
+#define PCS_MR_CONTROL_RES_10_10 0x0000000000000400ULL
+#define PCS_MR_CONTROL_RST_AN 0x0000000000000200ULL
+#define PCS_MR_CONTROL_DUPLEX 0x0000000000000100ULL
+#define PCS_MR_CONTROL_COLTST 0x0000000000000080ULL
+#define PCS_MR_CONTROL_SPDMSB 0x0000000000000040ULL
+#define PCS_MR_CONTROL_UNI 0x0000000000000020ULL
+#define PCS_MR_CONTROL_RES_0_4 0x000000000000001fULL
+
+#define PCS_MR_STATUS_RES_16_63 0xffffffffffff0000ULL
+#define PCS_MR_STATUS_HUN_T4 0x0000000000008000ULL
+#define PCS_MR_STATUS_HUN_XFD 0x0000000000004000ULL
+#define PCS_MR_STATUS_HUN_XHD 0x0000000000002000ULL
+#define PCS_MR_STATUS_TEN_FD 0x0000000000001000ULL
+#define PCS_MR_STATUS_TEN_HD 0x0000000000000800ULL
+#define PCS_MR_STATUS_HUN_T2FD 0x0000000000000400ULL
+#define PCS_MR_STATUS_HUN_T2HD 0x0000000000000200ULL
+#define PCS_MR_STATUS_EXT_ST 0x0000000000000100ULL
+#define PCS_MR_STATUS_RES_7_7 0x0000000000000080ULL
+#define PCS_MR_STATUS_PRB_SUP 0x0000000000000040ULL
+#define PCS_MR_STATUS_AN_CPT 0x0000000000000020ULL
+#define PCS_MR_STATUS_RM_FLT 0x0000000000000010ULL
+#define PCS_MR_STATUS_AN_ABIL 0x0000000000000008ULL
+#define PCS_MR_STATUS_LNK_ST 0x0000000000000004ULL
+#define PCS_MR_STATUS_RES_1_1 0x0000000000000002ULL
+#define PCS_MR_STATUS_EXTND 0x0000000000000001ULL
+
+#define PCS_LINK_TIMER_COUNT_MASK 0x000000000000ffffULL
+
+#define PCS_MISC_CTL_SGMII 0x0000000000001000ULL
+#define PCS_MISC_CTL_GMXENO 0x0000000000000800ULL
+#define PCS_MISC_CTL_LOOPBCK2 0x0000000000000400ULL
+#define PCS_MISC_CTL_MAC_PHY 0x0000000000000200ULL
+#define PCS_MISC_CTL_MODE 0x0000000000000100ULL
+#define PCS_MISC_CTL_AN_OVRD 0x0000000000000080ULL
+#define PCS_MISC_CTL_SAMP_PT 0x000000000000007fULL
#endif /* _CN30XXGMXREG_H_ */
diff --git a/sys/arch/octeon/dev/cn30xxgmxvar.h b/sys/arch/octeon/dev/cn30xxgmxvar.h
index f7477296533..09f6423fb95 100644
--- a/sys/arch/octeon/dev/cn30xxgmxvar.h
+++ b/sys/arch/octeon/dev/cn30xxgmxvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cn30xxgmxvar.h,v 1.5 2015/09/14 11:18:49 stsp Exp $ */
+/* $OpenBSD: cn30xxgmxvar.h,v 1.6 2016/06/09 15:29:22 visa Exp $ */
/*
* Copyright (c) 2007 Internet Initiative Japan, Inc.
@@ -40,7 +40,8 @@
#define GMX_MII_PORT 1
#define GMX_GMII_PORT 2
#define GMX_RGMII_PORT 3
-#define GMX_SPI42_PORT 4
+#define GMX_SGMII_PORT 4
+#define GMX_SPI42_PORT 5
#define GMX_FRM_MAX_SIZ 0x600
@@ -63,6 +64,7 @@ struct cn30xxgmx_port_softc {
struct cn30xxgmx_port_ops
*sc_port_ops;
struct cn30xxasx_softc *sc_port_asx;
+ bus_space_handle_t sc_port_pcs_regh;
struct cn30xxipd_softc *sc_ipd;
uint64_t sc_port_flowflags;
};
@@ -130,11 +132,11 @@ uint64_t cn30xxgmx_get_rx_int_reg(struct cn30xxgmx_port_softc *sc);
uint64_t cn30xxgmx_get_tx_int_reg(struct cn30xxgmx_port_softc *sc);
static inline int cn30xxgmx_link_status(struct cn30xxgmx_port_softc *);
-/* XXX RGMII specific */
static inline int
cn30xxgmx_link_status(struct cn30xxgmx_port_softc *sc)
{
- return (sc->sc_link & RXN_RX_INBND_STATUS) ? 1 : 0;
+ return ((sc->sc_port_mii->mii_media_status & (IFM_AVALID | IFM_ACTIVE))
+ == (IFM_AVALID | IFM_ACTIVE));
}
#endif
diff --git a/sys/arch/octeon/dev/cn30xxpip.c b/sys/arch/octeon/dev/cn30xxpip.c
index 00245e5f4aa..1d13d238aeb 100644
--- a/sys/arch/octeon/dev/cn30xxpip.c
+++ b/sys/arch/octeon/dev/cn30xxpip.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cn30xxpip.c,v 1.5 2016/05/19 15:42:02 visa Exp $ */
+/* $OpenBSD: cn30xxpip.c,v 1.6 2016/06/09 15:29:22 visa Exp $ */
/*
* Copyright (c) 2007 Internet Initiative Japan, Inc.
@@ -37,6 +37,7 @@
#include <machine/octeonvar.h>
+#include <octeon/dev/cn30xxgmxreg.h>
#include <octeon/dev/cn30xxpipreg.h>
#include <octeon/dev/cn30xxpipvar.h>
@@ -223,7 +224,7 @@ cn30xxpip_stats(struct cn30xxpip_softc *sc, struct ifnet *ifp, int gmx_port)
panic("%s: invalid argument. sc=%p, ifp=%p\n", __func__,
sc, ifp);
- if (gmx_port < 0 || gmx_port > 2) {
+ if (gmx_port < 0 || gmx_port >= GMX_PORT_NUNITS) {
printf("%s: invalid gmx_port %d\n", __func__, gmx_port);
return;
}