1*1808e7f7Sskrll /* $NetBSD: meson_dwmac.c,v 1.16 2024/10/13 08:55:24 skrll Exp $ */ 2912cfa14Sjmcneill 3912cfa14Sjmcneill /*- 4912cfa14Sjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5912cfa14Sjmcneill * All rights reserved. 6912cfa14Sjmcneill * 7912cfa14Sjmcneill * Redistribution and use in source and binary forms, with or without 8912cfa14Sjmcneill * modification, are permitted provided that the following conditions 9912cfa14Sjmcneill * are met: 10912cfa14Sjmcneill * 1. Redistributions of source code must retain the above copyright 11912cfa14Sjmcneill * notice, this list of conditions and the following disclaimer. 12912cfa14Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 13912cfa14Sjmcneill * notice, this list of conditions and the following disclaimer in the 14912cfa14Sjmcneill * documentation and/or other materials provided with the distribution. 15912cfa14Sjmcneill * 16912cfa14Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17912cfa14Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18912cfa14Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19912cfa14Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20912cfa14Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21912cfa14Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22912cfa14Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23912cfa14Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24912cfa14Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25912cfa14Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26912cfa14Sjmcneill * POSSIBILITY OF SUCH DAMAGE. 27912cfa14Sjmcneill */ 28912cfa14Sjmcneill 29912cfa14Sjmcneill #include <sys/cdefs.h> 30912cfa14Sjmcneill 31*1808e7f7Sskrll __KERNEL_RCSID(0, "$NetBSD: meson_dwmac.c,v 1.16 2024/10/13 08:55:24 skrll Exp $"); 32912cfa14Sjmcneill 33912cfa14Sjmcneill #include <sys/param.h> 34912cfa14Sjmcneill #include <sys/bus.h> 35912cfa14Sjmcneill #include <sys/device.h> 36912cfa14Sjmcneill #include <sys/intr.h> 37912cfa14Sjmcneill #include <sys/systm.h> 38912cfa14Sjmcneill #include <sys/gpio.h> 39f872a062Smsaitoh #include <sys/rndsource.h> 40912cfa14Sjmcneill 41912cfa14Sjmcneill #include <net/if.h> 42912cfa14Sjmcneill #include <net/if_ether.h> 43912cfa14Sjmcneill #include <net/if_media.h> 44912cfa14Sjmcneill 45912cfa14Sjmcneill #include <dev/mii/miivar.h> 46912cfa14Sjmcneill 47912cfa14Sjmcneill #include <dev/ic/dwc_gmac_var.h> 48912cfa14Sjmcneill #include <dev/ic/dwc_gmac_reg.h> 49912cfa14Sjmcneill 50912cfa14Sjmcneill #include <dev/fdt/fdtvar.h> 51912cfa14Sjmcneill 52e8f1247cSjmcneill #define PRG_ETHERNET_ADDR0 0x00 53e8f1247cSjmcneill #define CLKGEN_ENABLE __BIT(12) 54229af777Sjmcneill #define RMII_CLK_I_INVERTED __BIT(11) 55e8f1247cSjmcneill #define PHY_CLK_ENABLE __BIT(10) 56e8f1247cSjmcneill #define MP2_CLK_OUT_DIV __BITS(9,7) 57e8f1247cSjmcneill #define TX_CLK_DELAY __BITS(6,5) 58e8f1247cSjmcneill #define PHY_INTERFACE_SEL __BIT(0) 59912cfa14Sjmcneill 606e54367aSthorpej static const struct device_compatible_entry compat_data[] = { 616e54367aSthorpej { .compat = "amlogic,meson8b-dwmac" }, 626e54367aSthorpej { .compat = "amlogic,meson-gx-dwmac" }, 636e54367aSthorpej { .compat = "amlogic,meson-gxbb-dwmac" }, 646e54367aSthorpej { .compat = "amlogic,meson-axg-dwmac" }, 656e54367aSthorpej DEVICE_COMPAT_EOL 66912cfa14Sjmcneill }; 67912cfa14Sjmcneill 68912cfa14Sjmcneill static int 6965eadd88Sjdc meson_dwmac_reset_eth(const int phandle) 70912cfa14Sjmcneill { 71912cfa14Sjmcneill struct fdtbus_gpio_pin *pin_reset; 72912cfa14Sjmcneill const u_int *reset_delay_us; 73912cfa14Sjmcneill bool reset_active_low; 74912cfa14Sjmcneill int len, val; 75912cfa14Sjmcneill 7654291df9Sjdc pin_reset = fdtbus_gpio_acquire(phandle, "snps,reset-gpio", 7754291df9Sjdc GPIO_PIN_OUTPUT); 7865eadd88Sjdc if (pin_reset == NULL) 7965eadd88Sjdc return ENXIO; 8054291df9Sjdc 8165eadd88Sjdc reset_delay_us = fdtbus_get_prop(phandle, "snps,reset-delays-us", &len); 82912cfa14Sjmcneill if (reset_delay_us == NULL || len != 12) 83912cfa14Sjmcneill return ENXIO; 84912cfa14Sjmcneill 85912cfa14Sjmcneill reset_active_low = of_hasprop(phandle, "snps,reset-active-low"); 86912cfa14Sjmcneill 87912cfa14Sjmcneill val = reset_active_low ? 1 : 0; 88912cfa14Sjmcneill 89912cfa14Sjmcneill fdtbus_gpio_write(pin_reset, val); 90912cfa14Sjmcneill delay(be32toh(reset_delay_us[0])); 91912cfa14Sjmcneill fdtbus_gpio_write(pin_reset, !val); 92912cfa14Sjmcneill delay(be32toh(reset_delay_us[1])); 93912cfa14Sjmcneill fdtbus_gpio_write(pin_reset, val); 94912cfa14Sjmcneill delay(be32toh(reset_delay_us[2])); 95912cfa14Sjmcneill 96912cfa14Sjmcneill return 0; 97912cfa14Sjmcneill } 98912cfa14Sjmcneill 9965eadd88Sjdc static int 10065eadd88Sjdc meson_dwmac_reset_phy(const int phandle) 10165eadd88Sjdc { 10265eadd88Sjdc struct fdtbus_gpio_pin *pin_reset; 10365eadd88Sjdc const u_int *reset_assert_us, *reset_deassert_us, *reset_gpios; 10465eadd88Sjdc bool reset_active_low; 10565eadd88Sjdc int len, val; 10665eadd88Sjdc 10754291df9Sjdc pin_reset = fdtbus_gpio_acquire(phandle, "reset-gpios", 10854291df9Sjdc GPIO_PIN_OUTPUT); 10965eadd88Sjdc if (pin_reset == NULL) 11065eadd88Sjdc return ENXIO; 11165eadd88Sjdc 11265eadd88Sjdc reset_assert_us = fdtbus_get_prop(phandle, "reset-assert-us", &len); 11354291df9Sjdc if (reset_assert_us == NULL || len != 4) 11454291df9Sjdc return ENXIO; 11565eadd88Sjdc reset_deassert_us = fdtbus_get_prop(phandle, "reset-deassert-us", &len); 11654291df9Sjdc if (reset_deassert_us == NULL || len != 4) 11754291df9Sjdc return ENXIO; 11865eadd88Sjdc reset_gpios = fdtbus_get_prop(phandle, "reset-gpios", &len); 11954291df9Sjdc if (reset_gpios == NULL || len != 12) 12054291df9Sjdc return ENXIO; 12154291df9Sjdc 12254291df9Sjdc reset_active_low = be32toh(reset_gpios[2]); 12354291df9Sjdc 12454291df9Sjdc val = reset_active_low ? 1 : 0; 12554291df9Sjdc 12654291df9Sjdc fdtbus_gpio_write(pin_reset, val); 12754291df9Sjdc delay(be32toh(reset_assert_us[0])); 12854291df9Sjdc fdtbus_gpio_write(pin_reset, !val); 12954291df9Sjdc delay(be32toh(reset_deassert_us[0])); 13054291df9Sjdc 13154291df9Sjdc return 0; 13254291df9Sjdc } 13354291df9Sjdc 134e8f1247cSjmcneill static void 135e8f1247cSjmcneill meson_dwmac_set_mode_rgmii(int phandle, bus_space_tag_t bst, 136e8f1247cSjmcneill bus_space_handle_t bsh, struct clk *clkin) 137e8f1247cSjmcneill { 138e8f1247cSjmcneill u_int tx_delay; 139e8f1247cSjmcneill uint32_t val; 140e8f1247cSjmcneill 141655163beSryo #define DIV_ROUND_OFF(x, y) (((x) + (y) / 2) / (y)) 142655163beSryo const u_int div = DIV_ROUND_OFF(clk_get_rate(clkin), 250000000); 143e8f1247cSjmcneill 144e8f1247cSjmcneill if (of_getprop_uint32(phandle, "amlogic,tx-delay-ns", &tx_delay) != 0) 145e8f1247cSjmcneill tx_delay = 2; 146e8f1247cSjmcneill 147e8f1247cSjmcneill val = bus_space_read_4(bst, bsh, PRG_ETHERNET_ADDR0); 148e8f1247cSjmcneill val |= PHY_INTERFACE_SEL; 149e8f1247cSjmcneill val &= ~TX_CLK_DELAY; 150e8f1247cSjmcneill val |= __SHIFTIN((tx_delay >> 1), TX_CLK_DELAY); 151e8f1247cSjmcneill val &= ~MP2_CLK_OUT_DIV; 152e8f1247cSjmcneill val |= __SHIFTIN(div, MP2_CLK_OUT_DIV); 153e8f1247cSjmcneill val |= PHY_CLK_ENABLE; 154e8f1247cSjmcneill val |= CLKGEN_ENABLE; 155e8f1247cSjmcneill bus_space_write_4(bst, bsh, PRG_ETHERNET_ADDR0, val); 156e8f1247cSjmcneill } 157e8f1247cSjmcneill 158229af777Sjmcneill static void 159229af777Sjmcneill meson_dwmac_set_mode_rmii(int phandle, bus_space_tag_t bst, 160229af777Sjmcneill bus_space_handle_t bsh) 161229af777Sjmcneill { 162229af777Sjmcneill uint32_t val; 163229af777Sjmcneill 164229af777Sjmcneill val = bus_space_read_4(bst, bsh, PRG_ETHERNET_ADDR0); 165229af777Sjmcneill val &= ~PHY_INTERFACE_SEL; 166229af777Sjmcneill val |= RMII_CLK_I_INVERTED; 167229af777Sjmcneill val &= ~TX_CLK_DELAY; 168229af777Sjmcneill val |= CLKGEN_ENABLE; 169229af777Sjmcneill bus_space_write_4(bst, bsh, PRG_ETHERNET_ADDR0, val); 170229af777Sjmcneill } 171229af777Sjmcneill 172912cfa14Sjmcneill static int 173912cfa14Sjmcneill meson_dwmac_intr(void *arg) 174912cfa14Sjmcneill { 175912cfa14Sjmcneill struct dwc_gmac_softc * const sc = arg; 176912cfa14Sjmcneill 177912cfa14Sjmcneill return dwc_gmac_intr(sc); 178912cfa14Sjmcneill } 179912cfa14Sjmcneill 180912cfa14Sjmcneill static int 181912cfa14Sjmcneill meson_dwmac_match(device_t parent, cfdata_t cf, void *aux) 182912cfa14Sjmcneill { 183912cfa14Sjmcneill struct fdt_attach_args * const faa = aux; 184912cfa14Sjmcneill 1856e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data); 186912cfa14Sjmcneill } 187912cfa14Sjmcneill 188912cfa14Sjmcneill static void 189912cfa14Sjmcneill meson_dwmac_attach(device_t parent, device_t self, void *aux) 190912cfa14Sjmcneill { 191912cfa14Sjmcneill struct dwc_gmac_softc * const sc = device_private(self); 192912cfa14Sjmcneill struct fdt_attach_args * const faa = aux; 193912cfa14Sjmcneill const int phandle = faa->faa_phandle; 194655163beSryo int miiclk, phandle_phy, phy = MII_PHY_ANY; 195655163beSryo u_int miiclk_rate; 196e8f1247cSjmcneill bus_space_handle_t prgeth_bsh; 197912cfa14Sjmcneill struct fdtbus_reset *rst_gmac; 198e8f1247cSjmcneill struct clk *clk_gmac, *clk_in[2]; 199912cfa14Sjmcneill const char *phy_mode; 200912cfa14Sjmcneill char intrstr[128]; 201e8f1247cSjmcneill bus_addr_t addr[2]; 202e8f1247cSjmcneill bus_size_t size[2]; 203912cfa14Sjmcneill 204e8f1247cSjmcneill if (fdtbus_get_reg(phandle, 0, &addr[0], &size[0]) != 0 || 205e8f1247cSjmcneill fdtbus_get_reg(phandle, 1, &addr[1], &size[1]) != 0) { 206912cfa14Sjmcneill aprint_error(": couldn't get registers\n"); 207912cfa14Sjmcneill return; 208912cfa14Sjmcneill } 209912cfa14Sjmcneill 210912cfa14Sjmcneill sc->sc_dev = self; 211912cfa14Sjmcneill sc->sc_bst = faa->faa_bst; 212e8f1247cSjmcneill if (bus_space_map(sc->sc_bst, addr[0], size[0], 0, &sc->sc_bsh) != 0 || 213e8f1247cSjmcneill bus_space_map(sc->sc_bst, addr[1], size[1], 0, &prgeth_bsh) != 0) { 214912cfa14Sjmcneill aprint_error(": couldn't map registers\n"); 215912cfa14Sjmcneill return; 216912cfa14Sjmcneill } 217912cfa14Sjmcneill sc->sc_dmat = faa->faa_dmat; 218912cfa14Sjmcneill 219912cfa14Sjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 220912cfa14Sjmcneill aprint_error(": failed to decode interrupt\n"); 221912cfa14Sjmcneill return; 222912cfa14Sjmcneill } 223912cfa14Sjmcneill 224912cfa14Sjmcneill clk_gmac = fdtbus_clock_get(phandle, "stmmaceth"); 225e8f1247cSjmcneill clk_in[0] = fdtbus_clock_get(phandle, "clkin0"); 226e8f1247cSjmcneill clk_in[1] = fdtbus_clock_get(phandle, "clkin1"); 227e8f1247cSjmcneill if (clk_gmac == NULL || clk_in[0] == NULL || clk_in[1] == NULL) { 228912cfa14Sjmcneill aprint_error(": couldn't get clocks\n"); 229912cfa14Sjmcneill return; 230912cfa14Sjmcneill } 231912cfa14Sjmcneill 232912cfa14Sjmcneill rst_gmac = fdtbus_reset_get(phandle, "stmmaceth"); 233912cfa14Sjmcneill 234912cfa14Sjmcneill phy_mode = fdtbus_get_string(phandle, "phy-mode"); 235912cfa14Sjmcneill if (phy_mode == NULL) { 236912cfa14Sjmcneill aprint_error(": missing 'phy-mode' property\n"); 237912cfa14Sjmcneill return; 238912cfa14Sjmcneill } 239655163beSryo phandle_phy = fdtbus_get_phandle(phandle, "phy-handle"); 240655163beSryo if (phandle_phy > 0) { 241655163beSryo of_getprop_uint32(phandle_phy, "reg", &phy); 242655163beSryo } else { 243655163beSryo phandle_phy = phandle; 244655163beSryo } 245e8f1247cSjmcneill 246e0e63425Sjmcneill if (strncmp(phy_mode, "rgmii", 5) == 0) { 247e8f1247cSjmcneill meson_dwmac_set_mode_rgmii(phandle, sc->sc_bst, prgeth_bsh, clk_in[0]); 248229af777Sjmcneill } else if (strcmp(phy_mode, "rmii") == 0) { 249229af777Sjmcneill meson_dwmac_set_mode_rmii(phandle, sc->sc_bst, prgeth_bsh); 250912cfa14Sjmcneill } else { 251912cfa14Sjmcneill aprint_error(": unsupported phy-mode '%s'\n", phy_mode); 252912cfa14Sjmcneill return; 253912cfa14Sjmcneill } 254912cfa14Sjmcneill 255912cfa14Sjmcneill if (clk_enable(clk_gmac) != 0) { 256912cfa14Sjmcneill aprint_error(": couldn't enable clock\n"); 257912cfa14Sjmcneill return; 258912cfa14Sjmcneill } 259912cfa14Sjmcneill 260912cfa14Sjmcneill if (rst_gmac != NULL && fdtbus_reset_deassert(rst_gmac) != 0) { 261912cfa14Sjmcneill aprint_error(": couldn't de-assert reset\n"); 262912cfa14Sjmcneill return; 263912cfa14Sjmcneill } 264912cfa14Sjmcneill 265912cfa14Sjmcneill aprint_naive("\n"); 266912cfa14Sjmcneill aprint_normal(": Gigabit Ethernet Controller\n"); 267912cfa14Sjmcneill 26864e248edSryo if (fdtbus_intr_establish_xname(phandle, 0, IPL_NET, 269c9ba1e93Sskrll FDT_INTR_MPSAFE, meson_dwmac_intr, sc, 27064e248edSryo device_xname(sc->sc_dev)) == NULL) { 271912cfa14Sjmcneill aprint_error_dev(self, "failed to establish interrupt on %s\n", intrstr); 272912cfa14Sjmcneill return; 273912cfa14Sjmcneill } 274912cfa14Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr); 275912cfa14Sjmcneill 27665eadd88Sjdc /* 27765eadd88Sjdc * Depending on the DTS, we need to check either the "snps,...", 27865eadd88Sjdc * properties on the ethernet node, or the "reset-..." 27965eadd88Sjdc * properties on the phy node for the MAC reset information. 28065eadd88Sjdc */ 28165eadd88Sjdc 28265eadd88Sjdc if (of_hasprop(phandle, "snps,reset-gpio")) { 28365eadd88Sjdc if (meson_dwmac_reset_eth(phandle) != 0) 284*1808e7f7Sskrll aprint_error_dev(self, "reset failed\n"); 28565eadd88Sjdc } else { 28665eadd88Sjdc if (meson_dwmac_reset_phy(phandle_phy) != 0) 28765eadd88Sjdc aprint_error_dev(self, "PHY reset failed\n"); 28865eadd88Sjdc } 289912cfa14Sjmcneill 290655163beSryo miiclk_rate = clk_get_rate(clk_gmac); 291655163beSryo if (miiclk_rate > 250 * 1000 * 1000) 292655163beSryo miiclk = GMAC_MII_CLK_250_300M_DIV124; 293655163beSryo else if (miiclk_rate > 150 * 1000 * 1000) 294655163beSryo miiclk = GMAC_MII_CLK_150_250M_DIV102; 295655163beSryo else if (miiclk_rate > 100 * 1000 * 1000) 296655163beSryo miiclk = GMAC_MII_CLK_100_150M_DIV62; 297655163beSryo else if (miiclk_rate > 60 * 1000 * 1000) 298655163beSryo miiclk = GMAC_MII_CLK_60_100M_DIV42; 299655163beSryo else if (miiclk_rate > 35 * 1000 * 1000) 300655163beSryo miiclk = GMAC_MII_CLK_35_60M_DIV26; 301655163beSryo else 302655163beSryo miiclk = GMAC_MII_CLK_25_35M_DIV16; 303655163beSryo 304655163beSryo dwc_gmac_attach(sc, phy, miiclk); 305912cfa14Sjmcneill } 306912cfa14Sjmcneill 307912cfa14Sjmcneill CFATTACH_DECL_NEW(meson_dwmac, sizeof(struct dwc_gmac_softc), 308912cfa14Sjmcneill meson_dwmac_match, meson_dwmac_attach, NULL, NULL); 309