Author: jmcneill Date: Sat Jul 16 18:06:41 2016 New Revision: 302939 URL: https://svnweb.freebsd.org/changeset/base/302939
Log: Add support for Allwinner H3 EMAC. H3 EMAC is the same as A83T/A64 except the SoC includes an (optional) internal 10/100 PHY. Both internal and external PHYs are supported on H3 with this driver. Modified: head/sys/arm/allwinner/if_awg.c Modified: head/sys/arm/allwinner/if_awg.c ============================================================================== --- head/sys/arm/allwinner/if_awg.c Sat Jul 16 15:52:14 2016 (r302938) +++ head/sys/arm/allwinner/if_awg.c Sat Jul 16 18:06:41 2016 (r302939) @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sockio.h> #include <sys/module.h> #include <sys/taskqueue.h> +#include <sys/gpio.h> #include <net/bpf.h> #include <net/if.h> @@ -69,9 +70,10 @@ __FBSDID("$FreeBSD$"); #include <dev/extres/regulator/regulator.h> #include "miibus_if.h" +#include "gpio_if.h" -#define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) -#define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) +#define RD4(sc, reg) bus_read_4((sc)->res[_RES_EMAC], (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res[_RES_EMAC], (reg), (val)) #define AWG_LOCK(sc) mtx_lock(&(sc)->mtx) #define AWG_UNLOCK(sc) mtx_unlock(&(sc)->mtx); @@ -101,6 +103,25 @@ __FBSDID("$FreeBSD$"); #define TX_INTERVAL_DEFAULT 64 #define RX_BATCH_DEFAULT 64 +/* syscon EMAC clock register */ +#define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */ +#define EMAC_CLK_EPHY_ADDR_SHIFT 20 +#define EMAC_CLK_EPHY_LED_POL (1 << 17) /* H3 */ +#define EMAC_CLK_EPHY_SHUTDOWN (1 << 16) /* H3 */ +#define EMAC_CLK_EPHY_SELECT (1 << 15) /* H3 */ +#define EMAC_CLK_RMII_EN (1 << 13) +#define EMAC_CLK_ETXDC (0x7 << 10) +#define EMAC_CLK_ETXDC_SHIFT 10 +#define EMAC_CLK_ERXDC (0x1f << 5) +#define EMAC_CLK_ERXDC_SHIFT 5 +#define EMAC_CLK_PIT (0x1 << 2) +#define EMAC_CLK_PIT_MII (0 << 2) +#define EMAC_CLK_PIT_RGMII (1 << 2) +#define EMAC_CLK_SRC (0x3 << 0) +#define EMAC_CLK_SRC_MII (0 << 0) +#define EMAC_CLK_SRC_EXT_RGMII (1 << 0) +#define EMAC_CLK_SRC_RGMII (2 << 0) + /* Burst length of RX and TX DMA transfers */ static int awg_burst_len = BURST_LEN_DEFAULT; TUNABLE_INT("hw.awg.burst_len", &awg_burst_len); @@ -121,8 +142,14 @@ TUNABLE_INT("hw.awg.tx_interval", &awg_t static int awg_rx_batch = RX_BATCH_DEFAULT; TUNABLE_INT("hw.awg.rx_batch", &awg_rx_batch); +enum awg_type { + EMAC_A83T = 1, + EMAC_H3, +}; + static struct ofw_compat_data compat_data[] = { - { "allwinner,sun8i-a83t-emac", 1 }, + { "allwinner,sun8i-a83t-emac", EMAC_A83T }, + { "allwinner,sun8i-h3-emac", EMAC_H3 }, { NULL, 0 } }; @@ -151,8 +178,15 @@ struct awg_rxring { u_int cur; }; +enum { + _RES_EMAC, + _RES_IRQ, + _RES_SYSCON, + _RES_NITEMS +}; + struct awg_softc { - struct resource *res[2]; + struct resource *res[_RES_NITEMS]; struct mtx mtx; if_t ifp; device_t miibus; @@ -162,6 +196,7 @@ struct awg_softc { u_int mdc_div_ratio_m; int link; int if_flags; + enum awg_type type; struct awg_txring tx; struct awg_rxring rx; @@ -170,6 +205,7 @@ struct awg_softc { static struct resource_spec awg_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, { -1, 0 } }; @@ -1016,49 +1052,73 @@ awg_ioctl(if_t ifp, u_long cmd, caddr_t } static int -awg_setup_extres(device_t dev) +awg_setup_phy(device_t dev) { struct awg_softc *sc; - hwreset_t rst_ahb; - clk_t clk_ahb, clk_tx, clk_tx_parent; - regulator_t reg; + clk_t clk_tx, clk_tx_parent; const char *tx_parent_name; char *phy_type; phandle_t node; - uint64_t freq; - int error, div; + uint32_t reg, tx_delay, rx_delay; + int error; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); - rst_ahb = NULL; - clk_ahb = NULL; - clk_tx = NULL; - clk_tx_parent = NULL; - reg = NULL; - phy_type = NULL; - /* Get AHB clock and reset resources */ - error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb); - if (error != 0) { - device_printf(dev, "cannot get ahb reset\n"); - goto fail; - } - error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb); - if (error != 0) { - device_printf(dev, "cannot get ahb clock\n"); - goto fail; - } - - /* Configure PHY for MII or RGMII mode */ - if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) { - if (bootverbose) - device_printf(dev, "PHY type: %s\n", phy_type); + if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) == 0) + return (0); + if (bootverbose) + device_printf(dev, "PHY type: %s, conf mode: %s\n", phy_type, + sc->res[_RES_SYSCON] != NULL ? "reg" : "clk"); + + if (sc->res[_RES_SYSCON] != NULL) { + reg = bus_read_4(sc->res[_RES_SYSCON], 0); + reg &= ~(EMAC_CLK_PIT | EMAC_CLK_SRC | EMAC_CLK_RMII_EN); + if (strcmp(phy_type, "rgmii") == 0) + reg |= EMAC_CLK_PIT_RGMII | EMAC_CLK_SRC_RGMII; + else if (strcmp(phy_type, "rmii") == 0) + reg |= EMAC_CLK_RMII_EN; + else + reg |= EMAC_CLK_PIT_MII | EMAC_CLK_SRC_MII; + + if (OF_getencprop(node, "tx-delay", &tx_delay, + sizeof(tx_delay)) > 0) { + reg &= ~EMAC_CLK_ETXDC; + reg |= (tx_delay << EMAC_CLK_ETXDC_SHIFT); + } + if (OF_getencprop(node, "rx-delay", &rx_delay, + sizeof(rx_delay)) > 0) { + reg &= ~EMAC_CLK_ERXDC; + reg |= (rx_delay << EMAC_CLK_ERXDC_SHIFT); + } + + if (sc->type == EMAC_H3) { + if (OF_hasprop(node, "allwinner,use-internal-phy")) { + reg |= EMAC_CLK_EPHY_SELECT; + reg &= ~EMAC_CLK_EPHY_SHUTDOWN; + if (OF_hasprop(node, + "allwinner,leds-active-low")) + reg |= EMAC_CLK_EPHY_LED_POL; + else + reg &= ~EMAC_CLK_EPHY_LED_POL; + + /* Set internal PHY addr to 1 */ + reg &= ~EMAC_CLK_EPHY_ADDR; + reg |= (1 << EMAC_CLK_EPHY_ADDR_SHIFT); + } else { + reg &= ~EMAC_CLK_EPHY_SELECT; + } + } + + if (bootverbose) + device_printf(dev, "EMAC clock: 0x%08x\n", reg); + bus_write_4(sc->res[_RES_SYSCON], 0, reg); + } else { if (strcmp(phy_type, "rgmii") == 0) tx_parent_name = "emac_int_tx"; else tx_parent_name = "mii_phy_tx"; - OF_prop_free(phy_type); /* Get the TX clock */ error = clk_get_by_ofw_name(dev, 0, "tx", &clk_tx); @@ -1090,12 +1150,63 @@ awg_setup_extres(device_t dev) } } - /* Enable AHB clock */ + error = 0; + +fail: + OF_prop_free(phy_type); + return (error); +} + +static int +awg_setup_extres(device_t dev) +{ + struct awg_softc *sc; + hwreset_t rst_ahb, rst_ephy; + clk_t clk_ahb, clk_ephy; + regulator_t reg; + phandle_t node; + uint64_t freq; + int error, div; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + rst_ahb = rst_ephy = NULL; + clk_ahb = clk_ephy = NULL; + reg = NULL; + + /* Get AHB clock and reset resources */ + error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb reset\n"); + goto fail; + } + if (hwreset_get_by_ofw_name(dev, 0, "ephy", &rst_ephy) != 0) + rst_ephy = NULL; + error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb clock\n"); + goto fail; + } + if (clk_get_by_ofw_name(dev, 0, "ephy", &clk_ephy) != 0) + clk_ephy = NULL; + + /* Configure PHY for MII or RGMII mode */ + if (awg_setup_phy(dev) != 0) + goto fail; + + /* Enable clocks */ error = clk_enable(clk_ahb); if (error != 0) { device_printf(dev, "cannot enable ahb clock\n"); goto fail; } + if (clk_ephy != NULL) { + error = clk_enable(clk_ephy); + if (error != 0) { + device_printf(dev, "cannot enable ephy clock\n"); + goto fail; + } + } /* De-assert reset */ error = hwreset_deassert(rst_ahb); @@ -1103,6 +1214,13 @@ awg_setup_extres(device_t dev) device_printf(dev, "cannot de-assert ahb reset\n"); goto fail; } + if (rst_ephy != NULL) { + error = hwreset_deassert(rst_ephy); + if (error != 0) { + device_printf(dev, "cannot de-assert ephy reset\n"); + goto fail; + } + } /* Enable PHY regulator if applicable */ if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { @@ -1135,22 +1253,20 @@ awg_setup_extres(device_t dev) } if (bootverbose) - device_printf(dev, "AHB frequency %llu Hz, MDC div: 0x%x\n", - freq, sc->mdc_div_ratio_m); + device_printf(dev, "AHB frequency %ju Hz, MDC div: 0x%x\n", + (uintmax_t)freq, sc->mdc_div_ratio_m); return (0); fail: - OF_prop_free(phy_type); - if (reg != NULL) regulator_release(reg); - if (clk_tx_parent != NULL) - clk_release(clk_tx_parent); - if (clk_tx != NULL) - clk_release(clk_tx); + if (clk_ephy != NULL) + clk_release(clk_ephy); if (clk_ahb != NULL) clk_release(clk_ahb); + if (rst_ephy != NULL) + hwreset_release(rst_ephy); if (rst_ahb != NULL) hwreset_release(rst_ahb); return (error); @@ -1226,6 +1342,52 @@ awg_dump_regs(device_t dev) } #endif +#define GPIO_ACTIVE_LOW 1 + +static int +awg_phy_reset(device_t dev) +{ + pcell_t gpio_prop[4], delay_prop[3]; + phandle_t node, gpio_node; + device_t gpio; + uint32_t pin, flags; + uint32_t pin_value; + + node = ofw_bus_get_node(dev); + if (OF_getencprop(node, "allwinner,reset-gpio", gpio_prop, + sizeof(gpio_prop)) <= 0) + return (0); + + if (OF_getencprop(node, "allwinner,reset-delays-us", delay_prop, + sizeof(delay_prop)) <= 0) + return (ENXIO); + + gpio_node = OF_node_from_xref(gpio_prop[0]); + if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) + return (ENXIO); + + if (GPIO_MAP_GPIOS(gpio, node, gpio_node, nitems(gpio_prop) - 1, + gpio_prop + 1, &pin, &flags) != 0) + return (ENXIO); + + pin_value = GPIO_PIN_LOW; + if (OF_hasprop(node, "allwinner,reset-active-low")) + pin_value = GPIO_PIN_HIGH; + + if (flags & GPIO_ACTIVE_LOW) + pin_value = !pin_value; + + GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); + GPIO_PIN_SET(gpio, pin, pin_value); + DELAY(delay_prop[0]); + GPIO_PIN_SET(gpio, pin, !pin_value); + DELAY(delay_prop[1]); + GPIO_PIN_SET(gpio, pin, pin_value); + DELAY(delay_prop[2]); + + return (0); +} + static int awg_reset(device_t dev) { @@ -1234,6 +1396,12 @@ awg_reset(device_t dev) sc = device_get_softc(dev); + /* Reset PHY if necessary */ + if (awg_phy_reset(dev) != 0) { + device_printf(dev, "failed to reset PHY\n"); + return (ENXIO); + } + /* Soft reset all registers and logic */ WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST); @@ -1431,6 +1599,7 @@ awg_attach(device_t dev) int error; sc = device_get_softc(dev); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; node = ofw_bus_get_node(dev); if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) { @@ -1461,8 +1630,8 @@ awg_attach(device_t dev) return (error); /* Install interrupt handler */ - error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, - NULL, awg_intr, sc, &sc->ih); + error = bus_setup_intr(dev, sc->res[_RES_IRQ], + INTR_TYPE_NET | INTR_MPSAFE, NULL, awg_intr, sc, &sc->ih); if (error != 0) { device_printf(dev, "cannot setup interrupt handler\n"); return (error); _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"