aboutsummaryrefslogtreecommitdiff
path: root/sys/arm64/rockchip/if_dwc_rk.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm64/rockchip/if_dwc_rk.c')
-rw-r--r--sys/arm64/rockchip/if_dwc_rk.c625
1 files changed, 625 insertions, 0 deletions
diff --git a/sys/arm64/rockchip/if_dwc_rk.c b/sys/arm64/rockchip/if_dwc_rk.c
new file mode 100644
index 000000000000..eafd9e00caf0
--- /dev/null
+++ b/sys/arm64/rockchip/if_dwc_rk.c
@@ -0,0 +1,625 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/dwc/if_dwc.h>
+#include <dev/dwc/if_dwcvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/extres/syscon/syscon.h>
+
+#include "if_dwc_if.h"
+#include "syscon_if.h"
+
+#define RK3328_GRF_MAC_CON0 0x0900
+#define MAC_CON0_GMAC2IO_TX_DL_CFG_MASK 0x7F
+#define MAC_CON0_GMAC2IO_TX_DL_CFG_SHIFT 0
+#define MAC_CON0_GMAC2IO_RX_DL_CFG_MASK 0x7F
+#define MAC_CON0_GMAC2IO_RX_DL_CFG_SHIFT 7
+
+#define RK3328_GRF_MAC_CON1 0x0904
+#define MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA (1 << 0)
+#define MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA (1 << 1)
+#define MAC_CON1_GMAC2IO_GMII_CLK_SEL_MASK (3 << 11)
+#define MAC_CON1_GMAC2IO_GMII_CLK_SEL_125 (0 << 11)
+#define MAC_CON1_GMAC2IO_GMII_CLK_SEL_25 (3 << 11)
+#define MAC_CON1_GMAC2IO_GMII_CLK_SEL_2_5 (2 << 11)
+#define MAC_CON1_GMAC2IO_RMII_MODE_MASK (1 << 9)
+#define MAC_CON1_GMAC2IO_RMII_MODE (1 << 9)
+#define MAC_CON1_GMAC2IO_INTF_SEL_MASK (7 << 4)
+#define MAC_CON1_GMAC2IO_INTF_RMII (4 << 4)
+#define MAC_CON1_GMAC2IO_INTF_RGMII (1 << 4)
+#define MAC_CON1_GMAC2IO_RMII_CLK_SEL_MASK (1 << 7)
+#define MAC_CON1_GMAC2IO_RMII_CLK_SEL_25 (1 << 7)
+#define MAC_CON1_GMAC2IO_RMII_CLK_SEL_2_5 (0 << 7)
+#define MAC_CON1_GMAC2IO_MAC_SPEED_MASK (1 << 2)
+#define MAC_CON1_GMAC2IO_MAC_SPEED_100 (1 << 2)
+#define MAC_CON1_GMAC2IO_MAC_SPEED_10 (0 << 2)
+#define RK3328_GRF_MAC_CON2 0x0908
+#define RK3328_GRF_MACPHY_CON0 0x0B00
+#define MACPHY_CON0_CLK_50M_MASK (1 << 14)
+#define MACPHY_CON0_CLK_50M (1 << 14)
+#define MACPHY_CON0_RMII_MODE_MASK (3 << 6)
+#define MACPHY_CON0_RMII_MODE (1 << 6)
+#define RK3328_GRF_MACPHY_CON1 0x0B04
+#define MACPHY_CON1_RMII_MODE_MASK (1 << 9)
+#define MACPHY_CON1_RMII_MODE (1 << 9)
+#define RK3328_GRF_MACPHY_CON2 0x0B08
+#define RK3328_GRF_MACPHY_CON3 0x0B0C
+#define RK3328_GRF_MACPHY_STATUS 0x0B10
+
+#define RK3399_GRF_SOC_CON5 0xc214
+#define SOC_CON5_GMAC_CLK_SEL_MASK (3 << 4)
+#define SOC_CON5_GMAC_CLK_SEL_125 (0 << 4)
+#define SOC_CON5_GMAC_CLK_SEL_25 (3 << 4)
+#define SOC_CON5_GMAC_CLK_SEL_2_5 (2 << 4)
+#define RK3399_GRF_SOC_CON6 0xc218
+#define SOC_CON6_GMAC_TXCLK_DLY_ENA (1 << 7)
+#define SOC_CON6_TX_DL_CFG_MASK 0x7F
+#define SOC_CON6_TX_DL_CFG_SHIFT 0
+#define SOC_CON6_RX_DL_CFG_MASK 0x7F
+#define SOC_CON6_GMAC_RXCLK_DLY_ENA (1 << 15)
+#define SOC_CON6_RX_DL_CFG_SHIFT 8
+
+struct if_dwc_rk_softc;
+
+typedef void (*if_dwc_rk_set_delaysfn_t)(struct if_dwc_rk_softc *);
+typedef int (*if_dwc_rk_set_speedfn_t)(struct if_dwc_rk_softc *, int);
+typedef void (*if_dwc_rk_set_phy_modefn_t)(struct if_dwc_rk_softc *);
+typedef void (*if_dwc_rk_phy_powerupfn_t)(struct if_dwc_rk_softc *);
+
+struct if_dwc_rk_ops {
+ if_dwc_rk_set_delaysfn_t set_delays;
+ if_dwc_rk_set_speedfn_t set_speed;
+ if_dwc_rk_set_phy_modefn_t set_phy_mode;
+ if_dwc_rk_phy_powerupfn_t phy_powerup;
+};
+
+struct if_dwc_rk_softc {
+ struct dwc_softc base;
+ uint32_t tx_delay;
+ uint32_t rx_delay;
+ bool integrated_phy;
+ bool clock_in;
+ phandle_t phy_node;
+ struct syscon *grf;
+ struct if_dwc_rk_ops *ops;
+ /* Common clocks */
+ clk_t mac_clk_rx;
+ clk_t mac_clk_tx;
+ clk_t aclk_mac;
+ clk_t pclk_mac;
+ clk_t clk_stmmaceth;
+ /* RMII clocks */
+ clk_t clk_mac_ref;
+ clk_t clk_mac_refout;
+ /* PHY clock */
+ clk_t clk_phy;
+};
+
+static void rk3328_set_delays(struct if_dwc_rk_softc *sc);
+static int rk3328_set_speed(struct if_dwc_rk_softc *sc, int speed);
+static void rk3328_set_phy_mode(struct if_dwc_rk_softc *sc);
+static void rk3328_phy_powerup(struct if_dwc_rk_softc *sc);
+
+static void rk3399_set_delays(struct if_dwc_rk_softc *sc);
+static int rk3399_set_speed(struct if_dwc_rk_softc *sc, int speed);
+
+static struct if_dwc_rk_ops rk3288_ops = {
+};
+
+static struct if_dwc_rk_ops rk3328_ops = {
+ .set_delays = rk3328_set_delays,
+ .set_speed = rk3328_set_speed,
+ .set_phy_mode = rk3328_set_phy_mode,
+ .phy_powerup = rk3328_phy_powerup,
+};
+
+static struct if_dwc_rk_ops rk3399_ops = {
+ .set_delays = rk3399_set_delays,
+ .set_speed = rk3399_set_speed,
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"rockchip,rk3288-gmac", (uintptr_t)&rk3288_ops},
+ {"rockchip,rk3328-gmac", (uintptr_t)&rk3328_ops},
+ {"rockchip,rk3399-gmac", (uintptr_t)&rk3399_ops},
+ {NULL, 0}
+};
+
+static void
+rk3328_set_delays(struct if_dwc_rk_softc *sc)
+{
+ uint32_t reg;
+ uint32_t tx, rx;
+
+ if (sc->base.phy_mode != PHY_MODE_RGMII)
+ return;
+
+ reg = SYSCON_READ_4(sc->grf, RK3328_GRF_MAC_CON0);
+ tx = ((reg >> MAC_CON0_GMAC2IO_TX_DL_CFG_SHIFT) & MAC_CON0_GMAC2IO_TX_DL_CFG_MASK);
+ rx = ((reg >> MAC_CON0_GMAC2IO_RX_DL_CFG_SHIFT) & MAC_CON0_GMAC2IO_RX_DL_CFG_MASK);
+
+ reg = SYSCON_READ_4(sc->grf, RK3328_GRF_MAC_CON1);
+ if (bootverbose) {
+ device_printf(sc->base.dev, "current delays settings: tx=%u(%s) rx=%u(%s)\n",
+ tx, ((reg & MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA) ? "enabled" : "disabled"),
+ rx, ((reg & MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA) ? "enabled" : "disabled"));
+
+ device_printf(sc->base.dev, "setting new RK3328 RX/TX delays: %d/%d\n",
+ sc->tx_delay, sc->rx_delay);
+ }
+
+ reg = (MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA | MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA) << 16;
+ reg |= (MAC_CON1_GMAC2IO_GMAC_TXCLK_DLY_ENA | MAC_CON1_GMAC2IO_GMAC_RXCLK_DLY_ENA);
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON1, reg);
+
+ reg = 0xffff << 16;
+ reg |= ((sc->tx_delay & MAC_CON0_GMAC2IO_TX_DL_CFG_MASK) <<
+ MAC_CON0_GMAC2IO_TX_DL_CFG_SHIFT);
+ reg |= ((sc->rx_delay & MAC_CON0_GMAC2IO_TX_DL_CFG_MASK) <<
+ MAC_CON0_GMAC2IO_RX_DL_CFG_SHIFT);
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON0, reg);
+}
+
+static int
+rk3328_set_speed(struct if_dwc_rk_softc *sc, int speed)
+{
+ uint32_t reg;
+
+ switch (sc->base.phy_mode) {
+ case PHY_MODE_RGMII:
+ switch (speed) {
+ case IFM_1000_T:
+ case IFM_1000_SX:
+ reg = MAC_CON1_GMAC2IO_GMII_CLK_SEL_125;
+ break;
+ case IFM_100_TX:
+ reg = MAC_CON1_GMAC2IO_GMII_CLK_SEL_25;
+ break;
+ case IFM_10_T:
+ reg = MAC_CON1_GMAC2IO_GMII_CLK_SEL_2_5;
+ break;
+ default:
+ device_printf(sc->base.dev, "unsupported RGMII media %u\n", speed);
+ return (-1);
+ }
+
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON1,
+ ((MAC_CON1_GMAC2IO_GMII_CLK_SEL_MASK << 16) | reg));
+ break;
+ case PHY_MODE_RMII:
+ switch (speed) {
+ case IFM_100_TX:
+ reg = MAC_CON1_GMAC2IO_RMII_CLK_SEL_25 |
+ MAC_CON1_GMAC2IO_MAC_SPEED_100;
+ break;
+ case IFM_10_T:
+ reg = MAC_CON1_GMAC2IO_RMII_CLK_SEL_2_5 |
+ MAC_CON1_GMAC2IO_MAC_SPEED_10;
+ break;
+ default:
+ device_printf(sc->base.dev, "unsupported RMII media %u\n", speed);
+ return (-1);
+ }
+
+ SYSCON_WRITE_4(sc->grf,
+ sc->integrated_phy ? RK3328_GRF_MAC_CON2 : RK3328_GRF_MAC_CON1,
+ reg |
+ ((MAC_CON1_GMAC2IO_RMII_CLK_SEL_MASK | MAC_CON1_GMAC2IO_MAC_SPEED_MASK) << 16));
+ break;
+ }
+
+ return (0);
+}
+
+static void
+rk3328_set_phy_mode(struct if_dwc_rk_softc *sc)
+{
+
+ switch (sc->base.phy_mode) {
+ case PHY_MODE_RGMII:
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MAC_CON1,
+ ((MAC_CON1_GMAC2IO_INTF_SEL_MASK | MAC_CON1_GMAC2IO_RMII_MODE_MASK) << 16) |
+ MAC_CON1_GMAC2IO_INTF_RGMII);
+ break;
+ case PHY_MODE_RMII:
+ SYSCON_WRITE_4(sc->grf, sc->integrated_phy ? RK3328_GRF_MAC_CON2 : RK3328_GRF_MAC_CON1,
+ ((MAC_CON1_GMAC2IO_INTF_SEL_MASK | MAC_CON1_GMAC2IO_RMII_MODE_MASK) << 16) |
+ MAC_CON1_GMAC2IO_INTF_RMII | MAC_CON1_GMAC2IO_RMII_MODE);
+ break;
+ }
+}
+
+static void
+rk3328_phy_powerup(struct if_dwc_rk_softc *sc)
+{
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON1,
+ (MACPHY_CON1_RMII_MODE_MASK << 16) |
+ MACPHY_CON1_RMII_MODE);
+}
+
+static void
+rk3399_set_delays(struct if_dwc_rk_softc *sc)
+{
+ uint32_t reg, tx, rx;
+
+ if (sc->base.phy_mode != PHY_MODE_RGMII)
+ return;
+
+ reg = SYSCON_READ_4(sc->grf, RK3399_GRF_SOC_CON6);
+ tx = ((reg >> SOC_CON6_TX_DL_CFG_SHIFT) & SOC_CON6_TX_DL_CFG_MASK);
+ rx = ((reg >> SOC_CON6_RX_DL_CFG_SHIFT) & SOC_CON6_RX_DL_CFG_MASK);
+
+ if (bootverbose) {
+ device_printf(sc->base.dev, "current delays settings: tx=%u(%s) rx=%u(%s)\n",
+ tx, ((reg & SOC_CON6_GMAC_TXCLK_DLY_ENA) ? "enabled" : "disabled"),
+ rx, ((reg & SOC_CON6_GMAC_RXCLK_DLY_ENA) ? "enabled" : "disabled"));
+
+ device_printf(sc->base.dev, "setting new RK3399 RX/TX delays: %d/%d\n",
+ sc->rx_delay, sc->tx_delay);
+ }
+
+ reg = 0xFFFF << 16;
+ reg |= ((sc->tx_delay & SOC_CON6_TX_DL_CFG_MASK) <<
+ SOC_CON6_TX_DL_CFG_SHIFT);
+ reg |= ((sc->rx_delay & SOC_CON6_RX_DL_CFG_MASK) <<
+ SOC_CON6_RX_DL_CFG_SHIFT);
+ reg |= SOC_CON6_GMAC_TXCLK_DLY_ENA | SOC_CON6_GMAC_RXCLK_DLY_ENA;
+
+ SYSCON_WRITE_4(sc->grf, RK3399_GRF_SOC_CON6, reg);
+}
+
+static int
+rk3399_set_speed(struct if_dwc_rk_softc *sc, int speed)
+{
+ uint32_t reg;
+
+ switch (speed) {
+ case IFM_1000_T:
+ case IFM_1000_SX:
+ reg = SOC_CON5_GMAC_CLK_SEL_125;
+ break;
+ case IFM_100_TX:
+ reg = SOC_CON5_GMAC_CLK_SEL_25;
+ break;
+ case IFM_10_T:
+ reg = SOC_CON5_GMAC_CLK_SEL_2_5;
+ break;
+ default:
+ device_printf(sc->base.dev, "unsupported media %u\n", speed);
+ return (-1);
+ }
+
+ SYSCON_WRITE_4(sc->grf, RK3399_GRF_SOC_CON5,
+ ((SOC_CON5_GMAC_CLK_SEL_MASK << 16) | reg));
+ return (0);
+}
+
+static int
+if_dwc_rk_sysctl_delays(SYSCTL_HANDLER_ARGS)
+{
+ struct if_dwc_rk_softc *sc;
+ int rv;
+ uint32_t rxtx;
+
+ sc = arg1;
+ rxtx = ((sc->rx_delay << 8) | sc->tx_delay);
+
+ rv = sysctl_handle_int(oidp, &rxtx, 0, req);
+ if (rv != 0 || req->newptr == NULL)
+ return (rv);
+ sc->tx_delay = rxtx & 0xff;
+ sc->rx_delay = (rxtx >> 8) & 0xff;
+
+ if (sc->ops->set_delays)
+ sc->ops->set_delays(sc);
+
+ return (0);
+}
+
+static int
+if_dwc_rk_init_sysctl(struct if_dwc_rk_softc *sc)
+{
+ struct sysctl_oid *child;
+ struct sysctl_ctx_list *ctx_list;
+
+ ctx_list = device_get_sysctl_ctx(sc->base.dev);
+ child = device_get_sysctl_tree(sc->base.dev);
+ SYSCTL_ADD_PROC(ctx_list,
+ SYSCTL_CHILDREN(child), OID_AUTO, "delays",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, sc, 0,
+ if_dwc_rk_sysctl_delays, "", "RGMII RX/TX delays: ((rx << 8) | tx)");
+
+ return (0);
+}
+
+static int
+if_dwc_rk_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+ device_set_desc(dev, "Rockchip Gigabit Ethernet Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+if_dwc_rk_init_clocks(device_t dev)
+{
+ struct if_dwc_rk_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ error = clk_set_assigned(dev, ofw_bus_get_node(dev));
+ if (error != 0) {
+ device_printf(dev, "clk_set_assigned failed\n");
+ return (error);
+ }
+
+ /* Enable clocks */
+ error = clk_get_by_ofw_name(dev, 0, "stmmaceth", &sc->clk_stmmaceth);
+ if (error != 0) {
+ device_printf(dev, "could not find clock stmmaceth\n");
+ return (error);
+ }
+
+ if (clk_get_by_ofw_name(dev, 0, "mac_clk_rx", &sc->mac_clk_rx) != 0) {
+ device_printf(sc->base.dev, "could not get mac_clk_rx clock\n");
+ sc->mac_clk_rx = NULL;
+ }
+
+ if (clk_get_by_ofw_name(dev, 0, "mac_clk_tx", &sc->mac_clk_tx) != 0) {
+ device_printf(sc->base.dev, "could not get mac_clk_tx clock\n");
+ sc->mac_clk_tx = NULL;
+ }
+
+ if (clk_get_by_ofw_name(dev, 0, "aclk_mac", &sc->aclk_mac) != 0) {
+ device_printf(sc->base.dev, "could not get aclk_mac clock\n");
+ sc->aclk_mac = NULL;
+ }
+
+ if (clk_get_by_ofw_name(dev, 0, "pclk_mac", &sc->pclk_mac) != 0) {
+ device_printf(sc->base.dev, "could not get pclk_mac clock\n");
+ sc->pclk_mac = NULL;
+ }
+
+ if (sc->base.phy_mode == PHY_MODE_RGMII) {
+ if (clk_get_by_ofw_name(dev, 0, "clk_mac_ref", &sc->clk_mac_ref) != 0) {
+ device_printf(sc->base.dev, "could not get clk_mac_ref clock\n");
+ sc->clk_mac_ref = NULL;
+ }
+
+ if (!sc->clock_in) {
+ if (clk_get_by_ofw_name(dev, 0, "clk_mac_refout", &sc->clk_mac_refout) != 0) {
+ device_printf(sc->base.dev, "could not get clk_mac_refout clock\n");
+ sc->clk_mac_refout = NULL;
+ }
+
+ clk_set_freq(sc->clk_stmmaceth, 50000000, 0);
+ }
+ }
+
+ if ((sc->phy_node != 0) && sc->integrated_phy) {
+ if (clk_get_by_ofw_index(dev, sc->phy_node, 0, &sc->clk_phy) != 0) {
+ device_printf(sc->base.dev, "could not get PHY clock\n");
+ sc->clk_phy = NULL;
+ }
+
+ if (sc->clk_phy) {
+ clk_set_freq(sc->clk_phy, 50000000, 0);
+ }
+ }
+
+ if (sc->base.phy_mode == PHY_MODE_RMII) {
+ if (sc->mac_clk_rx)
+ clk_enable(sc->mac_clk_rx);
+ if (sc->clk_mac_ref)
+ clk_enable(sc->clk_mac_ref);
+ if (sc->clk_mac_refout)
+ clk_enable(sc->clk_mac_refout);
+ }
+ if (sc->clk_phy)
+ clk_enable(sc->clk_phy);
+ if (sc->aclk_mac)
+ clk_enable(sc->aclk_mac);
+ if (sc->pclk_mac)
+ clk_enable(sc->pclk_mac);
+ if (sc->mac_clk_tx)
+ clk_enable(sc->mac_clk_tx);
+
+ DELAY(50);
+
+ return (0);
+}
+
+static int
+if_dwc_rk_init(device_t dev)
+{
+ struct if_dwc_rk_softc *sc;
+ phandle_t node;
+ uint32_t rx, tx;
+ int err;
+ pcell_t phy_handle;
+ char *clock_in_out;
+ hwreset_t phy_reset;
+ regulator_t phy_supply;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ sc->ops = (struct if_dwc_rk_ops *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (OF_hasprop(node, "rockchip,grf") &&
+ syscon_get_by_ofw_property(dev, node,
+ "rockchip,grf", &sc->grf) != 0) {
+ device_printf(dev, "cannot get grf driver handle\n");
+ return (ENXIO);
+ }
+
+ if (OF_getencprop(node, "tx_delay", &tx, sizeof(tx)) <= 0)
+ tx = 0x30;
+ if (OF_getencprop(node, "rx_delay", &rx, sizeof(rx)) <= 0)
+ rx = 0x10;
+ sc->tx_delay = tx;
+ sc->rx_delay = rx;
+
+ sc->clock_in = true;
+ if (OF_getprop_alloc(node, "clock_in_out", (void **)&clock_in_out)) {
+ if (strcmp(clock_in_out, "input") == 0)
+ sc->clock_in = true;
+ else
+ sc->clock_in = false;
+ OF_prop_free(clock_in_out);
+ }
+
+ if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
+ sizeof(phy_handle)) > 0)
+ sc->phy_node = OF_node_from_xref(phy_handle);
+
+ if (sc->phy_node)
+ sc->integrated_phy = OF_hasprop(sc->phy_node, "phy-is-integrated");
+
+ if (sc->integrated_phy)
+ device_printf(sc->base.dev, "PHY is integrated\n");
+
+ if_dwc_rk_init_clocks(dev);
+
+ if (sc->ops->set_phy_mode)
+ sc->ops->set_phy_mode(sc);
+
+ if (sc->ops->set_delays)
+ sc->ops->set_delays(sc);
+
+ /*
+ * this also sets delays if tunable is defined
+ */
+ err = if_dwc_rk_init_sysctl(sc);
+ if (err != 0)
+ return (err);
+
+ if (regulator_get_by_ofw_property(sc->base.dev, 0,
+ "phy-supply", &phy_supply) == 0) {
+ if (regulator_enable(phy_supply)) {
+ device_printf(sc->base.dev,
+ "cannot enable 'phy' regulator\n");
+ }
+ }
+ else
+ device_printf(sc->base.dev, "no phy-supply property\n");
+
+ /* Power up */
+ if (sc->integrated_phy) {
+ if (sc->ops->phy_powerup)
+ sc->ops->phy_powerup(sc);
+
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON0,
+ (MACPHY_CON0_CLK_50M_MASK << 16) |
+ MACPHY_CON0_CLK_50M);
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON0,
+ (MACPHY_CON0_RMII_MODE_MASK << 16) |
+ MACPHY_CON0_RMII_MODE);
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON2, 0xffff1234);
+ SYSCON_WRITE_4(sc->grf, RK3328_GRF_MACPHY_CON3, 0x003f0035);
+
+ if (hwreset_get_by_ofw_idx(dev, sc->phy_node, 0, &phy_reset) == 0) {
+ hwreset_assert(phy_reset);
+ DELAY(20);
+ hwreset_deassert(phy_reset);
+ DELAY(20);
+ }
+ }
+
+ return (0);
+}
+
+static int
+if_dwc_rk_mac_type(device_t dev)
+{
+
+ return (DWC_GMAC_NORMAL_DESC);
+}
+
+static int
+if_dwc_rk_mii_clk(device_t dev)
+{
+
+ /* Should be calculated from the clock */
+ return (GMAC_MII_CLK_150_250M_DIV102);
+}
+
+static int
+if_dwc_rk_set_speed(device_t dev, int speed)
+{
+ struct if_dwc_rk_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->ops->set_speed)
+ return sc->ops->set_speed(sc, speed);
+
+ return (0);
+}
+
+static device_method_t if_dwc_rk_methods[] = {
+ DEVMETHOD(device_probe, if_dwc_rk_probe),
+
+ DEVMETHOD(if_dwc_init, if_dwc_rk_init),
+ DEVMETHOD(if_dwc_mac_type, if_dwc_rk_mac_type),
+ DEVMETHOD(if_dwc_mii_clk, if_dwc_rk_mii_clk),
+ DEVMETHOD(if_dwc_set_speed, if_dwc_rk_set_speed),
+
+ DEVMETHOD_END
+};
+
+static devclass_t dwc_rk_devclass;
+
+extern driver_t dwc_driver;
+
+DEFINE_CLASS_1(dwc, dwc_rk_driver, if_dwc_rk_methods,
+ sizeof(struct if_dwc_rk_softc), dwc_driver);
+DRIVER_MODULE(dwc_rk, simplebus, dwc_rk_driver, dwc_rk_devclass, 0, 0);
+MODULE_DEPEND(dwc_rk, dwc, 1, 1, 1);