1*c9ba1e93Sskrll /* $NetBSD: sunxi_gmac.c,v 1.11 2024/08/10 12:16:47 skrll Exp $ */ 26c2b6ecfSjmcneill 36c2b6ecfSjmcneill /*- 46c2b6ecfSjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 56c2b6ecfSjmcneill * All rights reserved. 66c2b6ecfSjmcneill * 76c2b6ecfSjmcneill * Redistribution and use in source and binary forms, with or without 86c2b6ecfSjmcneill * modification, are permitted provided that the following conditions 96c2b6ecfSjmcneill * are met: 106c2b6ecfSjmcneill * 1. Redistributions of source code must retain the above copyright 116c2b6ecfSjmcneill * notice, this list of conditions and the following disclaimer. 126c2b6ecfSjmcneill * 2. Redistributions in binary form must reproduce the above copyright 136c2b6ecfSjmcneill * notice, this list of conditions and the following disclaimer in the 146c2b6ecfSjmcneill * documentation and/or other materials provided with the distribution. 156c2b6ecfSjmcneill * 166c2b6ecfSjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 176c2b6ecfSjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 186c2b6ecfSjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 196c2b6ecfSjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 206c2b6ecfSjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 216c2b6ecfSjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 226c2b6ecfSjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 236c2b6ecfSjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 246c2b6ecfSjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 256c2b6ecfSjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 266c2b6ecfSjmcneill * POSSIBILITY OF SUCH DAMAGE. 276c2b6ecfSjmcneill */ 286c2b6ecfSjmcneill 296c2b6ecfSjmcneill #include <sys/cdefs.h> 306c2b6ecfSjmcneill 31*c9ba1e93Sskrll __KERNEL_RCSID(0, "$NetBSD: sunxi_gmac.c,v 1.11 2024/08/10 12:16:47 skrll Exp $"); 326c2b6ecfSjmcneill 336c2b6ecfSjmcneill #include <sys/param.h> 346c2b6ecfSjmcneill #include <sys/bus.h> 356c2b6ecfSjmcneill #include <sys/device.h> 366c2b6ecfSjmcneill #include <sys/intr.h> 376c2b6ecfSjmcneill #include <sys/systm.h> 38dbb9a751Sjmcneill #include <sys/gpio.h> 39f872a062Smsaitoh #include <sys/rndsource.h> 406c2b6ecfSjmcneill 416c2b6ecfSjmcneill #include <net/if.h> 426c2b6ecfSjmcneill #include <net/if_ether.h> 436c2b6ecfSjmcneill #include <net/if_media.h> 446c2b6ecfSjmcneill 456c2b6ecfSjmcneill #include <dev/mii/miivar.h> 466c2b6ecfSjmcneill 476c2b6ecfSjmcneill #include <dev/ic/dwc_gmac_var.h> 486c2b6ecfSjmcneill #include <dev/ic/dwc_gmac_reg.h> 496c2b6ecfSjmcneill 506c2b6ecfSjmcneill #include <dev/fdt/fdtvar.h> 516c2b6ecfSjmcneill 526c2b6ecfSjmcneill #define GMAC_TX_RATE_MII 25000000 536c2b6ecfSjmcneill #define GMAC_TX_RATE_RGMII 125000000 546c2b6ecfSjmcneill 556e54367aSthorpej static const struct device_compatible_entry compat_data[] = { 566e54367aSthorpej { .compat = "allwinner,sun7i-a20-gmac" }, 576e54367aSthorpej DEVICE_COMPAT_EOL 586c2b6ecfSjmcneill }; 596c2b6ecfSjmcneill 606c2b6ecfSjmcneill static int 61dbb9a751Sjmcneill sunxi_gmac_reset(const int phandle) 62dbb9a751Sjmcneill { 63dbb9a751Sjmcneill struct fdtbus_gpio_pin *pin_reset; 64dbb9a751Sjmcneill const u_int *reset_delay_us; 65dbb9a751Sjmcneill bool reset_active_low; 66dbb9a751Sjmcneill int len, val; 67dbb9a751Sjmcneill 68dbb9a751Sjmcneill pin_reset = fdtbus_gpio_acquire(phandle, "snps,reset-gpio", GPIO_PIN_OUTPUT); 69dbb9a751Sjmcneill if (pin_reset == NULL) 70dbb9a751Sjmcneill return 0; 71dbb9a751Sjmcneill 72dbb9a751Sjmcneill reset_delay_us = fdtbus_get_prop(phandle, "snps,reset-delays-us", &len); 73dbb9a751Sjmcneill if (reset_delay_us == NULL || len != 12) 74dbb9a751Sjmcneill return ENXIO; 75dbb9a751Sjmcneill 76dbb9a751Sjmcneill reset_active_low = of_hasprop(phandle, "snps,reset-active-low"); 77dbb9a751Sjmcneill 78dbb9a751Sjmcneill val = reset_active_low ? 1 : 0; 79dbb9a751Sjmcneill 80dbb9a751Sjmcneill fdtbus_gpio_write(pin_reset, val); 81dbb9a751Sjmcneill delay(be32toh(reset_delay_us[0])); 82dbb9a751Sjmcneill fdtbus_gpio_write(pin_reset, !val); 83dbb9a751Sjmcneill delay(be32toh(reset_delay_us[1])); 84dbb9a751Sjmcneill fdtbus_gpio_write(pin_reset, val); 85dbb9a751Sjmcneill delay(be32toh(reset_delay_us[2])); 86dbb9a751Sjmcneill 87dbb9a751Sjmcneill return 0; 88dbb9a751Sjmcneill } 89dbb9a751Sjmcneill 90dbb9a751Sjmcneill static int 916c2b6ecfSjmcneill sunxi_gmac_intr(void *arg) 926c2b6ecfSjmcneill { 936c2b6ecfSjmcneill return dwc_gmac_intr(arg); 946c2b6ecfSjmcneill } 956c2b6ecfSjmcneill 966c2b6ecfSjmcneill static int 97eb963c3bSbad sunxi_gmac_get_phyid(int phandle) 98eb963c3bSbad { 99eb963c3bSbad bus_addr_t addr; 100eb963c3bSbad int phy_phandle; 101eb963c3bSbad 102eb963c3bSbad phy_phandle = fdtbus_get_phandle(phandle, "phy"); 103eb963c3bSbad if (phy_phandle == -1) 104eb963c3bSbad phy_phandle = fdtbus_get_phandle(phandle, "phy-handle"); 105eb963c3bSbad if (phy_phandle == -1) 106eb963c3bSbad return MII_PHY_ANY; 107eb963c3bSbad 108eb963c3bSbad if (fdtbus_get_reg(phy_phandle, 0, &addr, NULL) != 0) 109eb963c3bSbad return MII_PHY_ANY; 110eb963c3bSbad 111eb963c3bSbad return (int)addr; 112eb963c3bSbad } 113eb963c3bSbad 114eb963c3bSbad static int 1156c2b6ecfSjmcneill sunxi_gmac_match(device_t parent, cfdata_t cf, void *aux) 1166c2b6ecfSjmcneill { 1176c2b6ecfSjmcneill struct fdt_attach_args * const faa = aux; 1186c2b6ecfSjmcneill 1196e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data); 1206c2b6ecfSjmcneill } 1216c2b6ecfSjmcneill 1226c2b6ecfSjmcneill static void 1236c2b6ecfSjmcneill sunxi_gmac_attach(device_t parent, device_t self, void *aux) 1246c2b6ecfSjmcneill { 1256c2b6ecfSjmcneill struct dwc_gmac_softc * const sc = device_private(self); 1266c2b6ecfSjmcneill struct fdt_attach_args * const faa = aux; 1276c2b6ecfSjmcneill const int phandle = faa->faa_phandle; 1286c2b6ecfSjmcneill struct clk *clk_gmac, *clk_gmac_tx; 129dbb9a751Sjmcneill struct fdtbus_reset *rst_gmac; 130d05cdcefSjmcneill struct fdtbus_regulator *reg_phy; 1316c2b6ecfSjmcneill const char *phy_mode; 1326c2b6ecfSjmcneill char intrstr[128]; 1336c2b6ecfSjmcneill bus_addr_t addr; 1346c2b6ecfSjmcneill bus_size_t size; 1356c2b6ecfSjmcneill 1366c2b6ecfSjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 1376c2b6ecfSjmcneill aprint_error(": couldn't get registers\n"); 1386c2b6ecfSjmcneill return; 1396c2b6ecfSjmcneill } 1406c2b6ecfSjmcneill 1416c2b6ecfSjmcneill sc->sc_dev = self; 1426c2b6ecfSjmcneill sc->sc_bst = faa->faa_bst; 1436c2b6ecfSjmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 1446c2b6ecfSjmcneill aprint_error(": couldn't map registers\n"); 1456c2b6ecfSjmcneill return; 1466c2b6ecfSjmcneill } 1476c2b6ecfSjmcneill sc->sc_dmat = faa->faa_dmat; 1486c2b6ecfSjmcneill 1496c2b6ecfSjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 1506c2b6ecfSjmcneill aprint_error(": failed to decode interrupt\n"); 1516c2b6ecfSjmcneill return; 1526c2b6ecfSjmcneill } 1536c2b6ecfSjmcneill 1546c2b6ecfSjmcneill clk_gmac = fdtbus_clock_get(phandle, "stmmaceth"); 1556c2b6ecfSjmcneill clk_gmac_tx = fdtbus_clock_get(phandle, "allwinner_gmac_tx"); 1566c2b6ecfSjmcneill if (clk_gmac == NULL || clk_gmac_tx == NULL) { 1576c2b6ecfSjmcneill aprint_error(": couldn't get clocks\n"); 1586c2b6ecfSjmcneill return; 1596c2b6ecfSjmcneill } 1606c2b6ecfSjmcneill 161dbb9a751Sjmcneill rst_gmac = fdtbus_reset_get(phandle, "stmmaceth"); 162dbb9a751Sjmcneill 1636c2b6ecfSjmcneill phy_mode = fdtbus_get_string(phandle, "phy-mode"); 1646c2b6ecfSjmcneill if (phy_mode == NULL) { 1656c2b6ecfSjmcneill aprint_error(": missing 'phy-mode' property\n"); 1666c2b6ecfSjmcneill return; 1676c2b6ecfSjmcneill } 168d05cdcefSjmcneill 169d05cdcefSjmcneill reg_phy = fdtbus_regulator_acquire(phandle, "phy-supply"); 170d05cdcefSjmcneill if (reg_phy != NULL && fdtbus_regulator_enable(reg_phy) != 0) { 171d05cdcefSjmcneill aprint_error(": couldn't enable PHY regualtor\n"); 172d05cdcefSjmcneill return; 173d05cdcefSjmcneill } 174d05cdcefSjmcneill 1756c2b6ecfSjmcneill if (strcmp(phy_mode, "mii") == 0) { 1766c2b6ecfSjmcneill if (clk_set_rate(clk_gmac_tx, GMAC_TX_RATE_MII) != 0) { 1776c2b6ecfSjmcneill aprint_error(": failed to set TX clock rate (MII)\n"); 1786c2b6ecfSjmcneill return; 1796c2b6ecfSjmcneill } 180e0e63425Sjmcneill } else if (strncmp(phy_mode, "rgmii", 5) == 0) { 1816c2b6ecfSjmcneill if (clk_set_rate(clk_gmac_tx, GMAC_TX_RATE_RGMII) != 0) { 1826c2b6ecfSjmcneill aprint_error(": failed to set TX clock rate (RGMII)\n"); 1836c2b6ecfSjmcneill return; 1846c2b6ecfSjmcneill } 1856c2b6ecfSjmcneill } else { 1866c2b6ecfSjmcneill aprint_error(": unsupported phy-mode '%s'\n", phy_mode); 1876c2b6ecfSjmcneill return; 1886c2b6ecfSjmcneill } 1896c2b6ecfSjmcneill 1906c2b6ecfSjmcneill if (clk_enable(clk_gmac_tx) != 0 || clk_enable(clk_gmac) != 0) { 1916c2b6ecfSjmcneill aprint_error(": couldn't enable clocks\n"); 1926c2b6ecfSjmcneill return; 1936c2b6ecfSjmcneill } 1946c2b6ecfSjmcneill 195dbb9a751Sjmcneill if (rst_gmac != NULL && fdtbus_reset_deassert(rst_gmac) != 0) { 196dbb9a751Sjmcneill aprint_error(": couldn't de-assert reset\n"); 197dbb9a751Sjmcneill return; 198dbb9a751Sjmcneill } 199dbb9a751Sjmcneill 2006c2b6ecfSjmcneill aprint_naive("\n"); 2016c2b6ecfSjmcneill aprint_normal(": GMAC\n"); 2026c2b6ecfSjmcneill 203076a1169Sjmcneill if (fdtbus_intr_establish_xname(phandle, 0, IPL_NET, 204*c9ba1e93Sskrll FDT_INTR_MPSAFE, sunxi_gmac_intr, sc, 205076a1169Sjmcneill device_xname(self)) == NULL) { 2066c2b6ecfSjmcneill aprint_error_dev(self, "failed to establish interrupt on %s\n", intrstr); 2076c2b6ecfSjmcneill return; 2086c2b6ecfSjmcneill } 2096c2b6ecfSjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr); 2106c2b6ecfSjmcneill 211dbb9a751Sjmcneill if (sunxi_gmac_reset(phandle) != 0) 212dbb9a751Sjmcneill aprint_error_dev(self, "PHY reset failed\n"); 213dbb9a751Sjmcneill 214eb963c3bSbad dwc_gmac_attach(sc, sunxi_gmac_get_phyid(phandle), 215eb963c3bSbad GMAC_MII_CLK_150_250M_DIV102); 2166c2b6ecfSjmcneill } 2176c2b6ecfSjmcneill 2186c2b6ecfSjmcneill CFATTACH_DECL_NEW(sunxi_gmac, sizeof(struct dwc_gmac_softc), 2196c2b6ecfSjmcneill sunxi_gmac_match, sunxi_gmac_attach, NULL, NULL); 220