svn commit: r302939 - head/sys/arm/allwinner

Jared McNeill jmcneill at FreeBSD.org
Sat Jul 16 18:06:43 UTC 2016


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);


More information about the svn-src-head mailing list