diff options
author | Ian Lepore <ian@FreeBSD.org> | 2014-07-14 20:58:57 +0000 |
---|---|---|
committer | Ian Lepore <ian@FreeBSD.org> | 2014-07-14 20:58:57 +0000 |
commit | 0f822edeadd0524a52d971da558372f4e6ba20f7 (patch) | |
tree | dbf4d22a4d65263708c07edbe1f8c59f63db43c7 /sys/dev | |
parent | ea4cbe3197c295bb38dbaeefe39fef4943a53606 (diff) | |
download | src-0f822edeadd0524a52d971da558372f4e6ba20f7.tar.gz src-0f822edeadd0524a52d971da558372f4e6ba20f7.zip |
Fix the Zedboard/Zynq ethernet driver to handle media speed changes so
that it can connect to switches at speeds other than 1gb.
This requires changing the reference clock speed. Since we still don't
have a general clock API that lets a SoC-independant driver manipulate its
own clocks, this change includes a weak reference to a routine named
cgem_set_ref_clk(). The default implementation is a no-op; SoC-specific
code can provide an implementation that actually changes the speed.
Submitted by: Thomas Skibo <ThomasSkibo@sbcglobal.net>
Notes
Notes:
svn path=/head/; revision=268633
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/cadence/if_cgem.c | 104 |
1 files changed, 71 insertions, 33 deletions
diff --git a/sys/dev/cadence/if_cgem.c b/sys/dev/cadence/if_cgem.c index 18a3ecc6c1c7..593478a6db8e 100644 --- a/sys/dev/cadence/if_cgem.c +++ b/sys/dev/cadence/if_cgem.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2012-2013 Thomas Skibo + * Copyright (c) 2012-2014 Thomas Skibo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -108,6 +108,7 @@ struct cgem_softc { void *intrhand; struct callout tick_ch; uint32_t net_ctl_shadow; + int ref_clk_num; u_char eaddr[6]; bus_dma_tag_t desc_dma_tag; @@ -149,6 +150,9 @@ struct cgem_softc { #define CGEM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define CGEM_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +/* Allow platforms to optionally provide a way to set the reference clock. */ +int cgem_set_ref_clk(int unit, int frequency); + static devclass_t cgem_devclass; static int cgem_probe(device_t dev); @@ -707,47 +711,18 @@ cgem_start(struct ifnet *ifp) CGEM_UNLOCK(sc); } -/* Respond to changes in media. */ -static void -cgem_media_update(struct cgem_softc *sc, int active) -{ - uint32_t net_cfg; - - CGEM_ASSERT_LOCKED(sc); - - /* Update hardware to reflect phy status. */ - net_cfg = RD4(sc, CGEM_NET_CFG); - net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN | - CGEM_NET_CFG_FULL_DUPLEX); - - if (IFM_SUBTYPE(active) == IFM_1000_T) - net_cfg |= (CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN); - else if (IFM_SUBTYPE(active) == IFM_100_TX) - net_cfg |= CGEM_NET_CFG_SPEED100; - - if ((active & IFM_FDX) != 0) - net_cfg |= CGEM_NET_CFG_FULL_DUPLEX; - WR4(sc, CGEM_NET_CFG, net_cfg); -} - static void cgem_tick(void *arg) { struct cgem_softc *sc = (struct cgem_softc *)arg; struct mii_data *mii; - int active; CGEM_ASSERT_LOCKED(sc); /* Poll the phy. */ if (sc->miibus != NULL) { mii = device_get_softc(sc->miibus); - active = mii->mii_media_active; mii_tick(mii); - if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID) && - active != mii->mii_media_active) - cgem_media_update(sc, mii->mii_media_active); } /* Next callout in one second. */ @@ -894,7 +869,6 @@ cgem_init_locked(struct cgem_softc *sc) mii = device_get_softc(sc->miibus); mii_pollstat(mii); - cgem_media_update(sc, mii->mii_media_active); cgem_start_locked(sc->ifp); callout_reset(&sc->tick_ch, hz, cgem_tick, sc); @@ -1073,12 +1047,13 @@ cgem_ifmedia_upd(struct ifnet *ifp) { struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc; struct mii_data *mii; + int error; mii = device_get_softc(sc->miibus); CGEM_LOCK(sc); - mii_mediachg(mii); + error = mii_mediachg(mii); CGEM_UNLOCK(sc); - return (0); + return (error); } static void @@ -1148,6 +1123,60 @@ cgem_miibus_writereg(device_t dev, int phy, int reg, int data) return (0); } +/* + * Overridable weak symbol cgem_set_ref_clk(). This allows platforms to + * provide a function to set the cgem's reference clock. + */ +static int __used +cgem_default_set_ref_clk(int unit, int frequency) +{ + + return 0; +} +__weak_reference(cgem_default_set_ref_clk, cgem_set_ref_clk); + +static void +cgem_miibus_statchg(device_t dev) +{ + struct cgem_softc *sc; + struct mii_data *mii; + uint32_t net_cfg; + int ref_clk_freq; + + sc = device_get_softc(dev); + + mii = device_get_softc(sc->miibus); + + if ((mii->mii_media_status & IFM_AVALID) != 0) { + /* Update hardware to reflect phy status. */ + net_cfg = RD4(sc, CGEM_NET_CFG); + net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN | + CGEM_NET_CFG_FULL_DUPLEX); + + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: + net_cfg |= (CGEM_NET_CFG_SPEED100 | + CGEM_NET_CFG_GIGE_EN); + ref_clk_freq = 125000000; + break; + case IFM_100_TX: + net_cfg |= CGEM_NET_CFG_SPEED100; + ref_clk_freq = 25000000; + break; + default: + ref_clk_freq = 2500000; + } + + if ((mii->mii_media_active & IFM_FDX) != 0) + net_cfg |= CGEM_NET_CFG_FULL_DUPLEX; + WR4(sc, CGEM_NET_CFG, net_cfg); + + /* Set the reference clock if necessary. */ + if (cgem_set_ref_clk(sc->ref_clk_num, ref_clk_freq)) + device_printf(dev, "could not set ref clk%d to %d.\n", + sc->ref_clk_num, ref_clk_freq); + } +} static int cgem_probe(device_t dev) @@ -1165,12 +1194,20 @@ cgem_attach(device_t dev) { struct cgem_softc *sc = device_get_softc(dev); struct ifnet *ifp = NULL; + phandle_t node; + pcell_t cell; int rid, err; u_char eaddr[ETHER_ADDR_LEN]; sc->dev = dev; CGEM_LOCK_INIT(sc); + /* Get reference clock number and base divider from fdt. */ + node = ofw_bus_get_node(dev); + sc->ref_clk_num = 0; + if (OF_getprop(node, "ref-clock-num", &cell, sizeof(cell)) > 0) + sc->ref_clk_num = fdt32_to_cpu(cell); + /* Get memory resource. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, @@ -1372,6 +1409,7 @@ static device_method_t cgem_methods[] = { /* MII interface */ DEVMETHOD(miibus_readreg, cgem_miibus_readreg), DEVMETHOD(miibus_writereg, cgem_miibus_writereg), + DEVMETHOD(miibus_statchg, cgem_miibus_statchg), DEVMETHOD_END }; |