Index: arm/conf/IMX6 =================================================================== --- arm/conf/IMX6 +++ arm/conf/IMX6 @@ -63,6 +63,10 @@ device miibus # Required for ethernet device bpf # Berkeley packet filter (required for DHCP) +# EXT_RESOURCES pseudo devices +options EXT_RESOURCES +device regulator + # General-purpose input/output device gpio Index: arm/freescale/imx/imx_gpio.c =================================================================== --- arm/freescale/imx/imx_gpio.c +++ arm/freescale/imx/imx_gpio.c @@ -800,5 +800,5 @@ }; static devclass_t imx51_gpio_devclass; -DRIVER_MODULE(imx51_gpio, simplebus, imx51_gpio_driver, imx51_gpio_devclass, - 0, 0); +EARLY_DRIVER_MODULE(imx51_gpio, simplebus, imx51_gpio_driver, imx51_gpio_devclass, + 0, 0, BUS_PASS_CPU); Index: dev/ffec/if_ffec.c =================================================================== --- dev/ffec/if_ffec.c +++ dev/ffec/if_ffec.c @@ -66,6 +66,7 @@ #include #include #include +#include #include @@ -84,8 +85,12 @@ #include #include #include +#include + #include "miibus_if.h" +#include + /* * There are small differences in the hardware on various SoCs. Not every SoC * we support has its own FECTYPE; most work as GENERIC and only the ones that @@ -179,6 +184,8 @@ uint32_t tx_idx_head; uint32_t tx_idx_tail; int txcount; + + gpio_pin_t phy_reset; }; #define FFEC_LOCK(sc) mtx_lock(&(sc)->mtx) @@ -245,6 +252,36 @@ *(bus_addr_t *)arg = segs[0].ds_addr; } +static int ar8035_phy_fixup(struct ffec_softc *sc, int phy) +{ + int val; + + /* To enable AR8031/AR8035, we must output a 125MHz clk from CLK_25M */ + MIIBUS_WRITEREG(sc->dev, phy, 0xd, 0x7); // dev addr + MIIBUS_WRITEREG(sc->dev, phy, 0xe, 0x8016); // reg addr + MIIBUS_WRITEREG(sc->dev, phy, 0xd, 0x4007); // ctl + dev addr + + val = MIIBUS_READREG(sc->dev, phy, 0xe); + val &= 0xffe7; + val |= 0x18; // set CLK_25M to 125MHz + MIIBUS_WRITEREG(sc->dev, phy, 0xe, val); + + /* introduce tx clock delay */ + MIIBUS_WRITEREG(sc->dev, phy, 0x1d, 0x5); + val = MIIBUS_READREG(sc->dev, phy, 0x1e); + val |= 0x0100; + MIIBUS_WRITEREG(sc->dev, phy, 0x1e, val); + + /* check phy power. if off, turn power up. */ + val = MIIBUS_READREG(sc->dev, phy, 0x0); + if (val & BMCR_PDOWN) { + device_printf(sc->dev, "Device was powered down Powering it up.\n"); + MIIBUS_WRITEREG(sc->dev, phy, 0x0, val & (~BMCR_PDOWN)); + } + + return 0; +} + static void ffec_miigasket_setup(struct ffec_softc *sc) { @@ -1371,6 +1408,11 @@ ether_ifdetach(sc->ifp); } + /* Release the phy-reset pin, if any */ + if(sc->phy_reset) { + gpio_pin_release(sc->phy_reset); + } + /* XXX no miibus detach? */ /* Clean up RX DMA resources and free mbufs. */ @@ -1432,6 +1474,9 @@ uint8_t eaddr[ETHER_ADDR_LEN]; char phy_conn_name[32]; uint32_t idx, mscr; + mii_attach_args_t *phy_dev; + device_t *children; + int nchildren, i; sc = device_get_softc(dev); sc->dev = dev; @@ -1471,6 +1516,33 @@ callout_init_mtx(&sc->ffec_callout, &sc->mtx, 0); + // do a reset + error = gpio_pin_get_by_ofw_property(dev, ofw_node, "phy-reset-gpios", &sc->phy_reset); + if(error != 0) { + sc->phy_reset = NULL; // the phy-reset is optional + } + + if(sc->phy_reset) { + gpio_pin_setflags(sc->phy_reset, GPIO_PIN_OUTPUT); + + error = gpio_pin_set_active(sc->phy_reset, true); + if(error != 0) { + device_printf(dev, "unable to hw reset phy: enable"); + return error; + } + + /* + * The dts does not specify for how long the PHY reset pin should be set, + * but 1ms seems to be enough is enough. + */ + DELAY(1000); + gpio_pin_set_active(sc->phy_reset, false); + if(error != 0) { + device_printf(dev, "unable to hw reset phy: disable"); + return error; + } + } + /* Allocate bus resources for accessing the hardware. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, @@ -1640,37 +1712,44 @@ goto out; } - /* - * Set up the PHY control register. + /** + * Calculates the correct MII speed to achieve about 2.5 MHz. * - * Speed formula for ENET is md_clock = mac_clock / ((N + 1) * 2). - * Speed formula for FEC is md_clock = mac_clock / (N * 2) - * - * XXX - Revisit this... - * - * For a Wandboard imx6 (ENET) I was originally using 4, but the uboot - * code uses 10. Both values seem to work, but I suspect many modern - * PHY parts can do mdio at speeds far above the standard 2.5 MHz. - * - * Different imx manuals use confusingly different terminology (things - * like "system clock" and "internal module clock") with examples that - * use frequencies that have nothing to do with ethernet, giving the - * vague impression that maybe the clock in question is the periphclock - * or something. In fact, on an imx53 development board (FEC), - * measuring the mdio clock at the pin on the PHY and playing with - * various divisors showed that the root speed was 66 MHz (clk_ipg_root - * aka periphclock) and 13 was the right divisor. + * 2.5 MHz (= clk_get_rate() / 2 * phy_speed) + */ + uint32_t mii_speed = howmany(imx_ccm_ipg_hz(), 5000000); + mii_speed--; + if (mii_speed > 63) { + device_printf(dev, + "clock (%u) to fast to get right mii speed\n", + imx_ccm_ipg_hz()); + return EINVAL; + } + if(bootverbose) + device_printf(dev, "mii_speed = %u\n", mii_speed); + + /* + * Somes devices also have a HOLDTIME field on their register. This + * field specified the amount of cycles that a value must be hold + * on the MDIO output. * - * All in all, it seems likely that 13 is a safe divisor for now, - * because if we really do need to base it on the peripheral clock - * speed, then we need a platform-independant get-clock-freq API. + * IEEE802.3 clause 22 defines a minimum of 10 ns for the hold time + * on the MDIO output. */ - mscr = 13 << FEC_MSCR_MII_SPEED_SHIFT; + uint32_t holdtime = howmany(imx_ccm_ipg_hz(), 100000000) - 1; + if(bootverbose) + device_printf(dev, "holdtime = %u\n", holdtime); + + mscr = ((mii_speed << FEC_MSCR_MII_SPEED_SHIFT) & FEC_MSCR_MII_SPEED_MASK) | + ((holdtime << FEC_MSCR_HOLDTIME_SHIFT) & FEC_MSCR_HOLDTIME_MASK); + + /* And some devices also allow to disable MII preambles */ if (OF_hasprop(ofw_node, "phy-disable-preamble")) { mscr |= FEC_MSCR_DIS_PRE; - if (bootverbose) + if(bootverbose) device_printf(dev, "PHY preamble disabled\n"); } + WR4(sc, FEC_MSCR_REG, mscr); /* Set up the ethernet interface. */ @@ -1710,6 +1789,30 @@ } sc->mii_softc = device_get_softc(sc->miibus); + /* + * AR8035 (as used in newer Wandboard REV d1) requires a small fixup: we need + * to configure the CLK_25M to output a 125MHz clock, as expected by i.MX6. + * + * This checks if the device is a AR8035 from its mii device id and executes + * the fixup if required. + */ + if (device_get_children(sc->miibus, &children, &nchildren) == 0) { + for (i = 0; i < nchildren; i++) { + /* XXX is there a better way to do this? */ + phy_dev = device_get_ivars(children[i]); + if(phy_dev == NULL) { + continue; + } + + if(phy_dev->mii_id1 == 0x004D && phy_dev->mii_id2 == 0xD072) { + if(bootverbose) + device_printf(dev, "PHY %u is AR8035, running fixup\n", phy_dev->mii_phyno); + ar8035_phy_fixup(sc, phy_dev->mii_phyno); + } + } + free(children, M_TEMP); + } + /* All ready to run, attach the ethernet interface. */ ether_ifattach(ifp, eaddr); sc->is_attached = true; @@ -1776,3 +1879,4 @@ MODULE_DEPEND(ffec, ether, 1, 1, 1); MODULE_DEPEND(ffec, miibus, 1, 1, 1); + Index: dev/ffec/if_ffecreg.h =================================================================== --- dev/ffec/if_ffecreg.h +++ dev/ffec/if_ffecreg.h @@ -95,7 +95,7 @@ #define FEC_MSCR_HOLDTIME_MASK (0x07 << FEC_MSCR_HOLDTIME_SHIFT) #define FEC_MSCR_DIS_PRE (1 << 7) #define FEC_MSCR_MII_SPEED_SHIFT 1 -#define FEC_MSCR_MII_SPEED_MASk (0x3f << FEC_MSCR_MII_SPEED_SHIFT) +#define FEC_MSCR_MII_SPEED_MASK (0x3f << FEC_MSCR_MII_SPEED_SHIFT) #define FEC_MIBC_REG 0x0064 #define FEC_MIBC_DIS (1U << 31)