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", &reg) == 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"

Reply via email to