1*ac5c9969Smrg /* $NetBSD: if_enet_imx.c,v 1.7 2022/12/27 18:55:06 mrg Exp $ */
28644267aSskrll
38644267aSskrll /*-
48644267aSskrll * Copyright (c) 2019 Genetec Corporation. All rights reserved.
58644267aSskrll * Written by Hashimoto Kenichi for Genetec Corporation.
68644267aSskrll *
78644267aSskrll * Redistribution and use in source and binary forms, with or without
88644267aSskrll * modification, are permitted provided that the following conditions
98644267aSskrll * are met:
108644267aSskrll * 1. Redistributions of source code must retain the above copyright
118644267aSskrll * notice, this list of conditions and the following disclaimer.
128644267aSskrll * 2. Redistributions in binary form must reproduce the above copyright
138644267aSskrll * notice, this list of conditions and the following disclaimer in the
148644267aSskrll * documentation and/or other materials provided with the distribution.
158644267aSskrll *
168644267aSskrll * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178644267aSskrll * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188644267aSskrll * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198644267aSskrll * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208644267aSskrll * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
218644267aSskrll * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
228644267aSskrll * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
238644267aSskrll * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
248644267aSskrll * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258644267aSskrll * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268644267aSskrll * SUCH DAMAGE.
278644267aSskrll */
288644267aSskrll
298644267aSskrll #include <sys/cdefs.h>
30*ac5c9969Smrg __KERNEL_RCSID(0, "$NetBSD: if_enet_imx.c,v 1.7 2022/12/27 18:55:06 mrg Exp $");
318644267aSskrll
328644267aSskrll #include "opt_fdt.h"
338644267aSskrll
348644267aSskrll #include <sys/param.h>
358644267aSskrll #include <sys/bus.h>
368644267aSskrll #include <sys/device.h>
378644267aSskrll
388644267aSskrll #include <arm/imx/if_enetreg.h>
398644267aSskrll #include <arm/imx/if_enetvar.h>
408644267aSskrll
418644267aSskrll #include <dev/fdt/fdtvar.h>
428644267aSskrll
438644267aSskrll struct enet_fdt_softc {
448644267aSskrll struct enet_softc sc_enet;
458644267aSskrll
468644267aSskrll struct fdtbus_gpio_pin *sc_pin_reset;
478644267aSskrll };
488644267aSskrll
498644267aSskrll CFATTACH_DECL_NEW(enet_fdt, sizeof(struct enet_fdt_softc),
508644267aSskrll enet_match, enet_attach, NULL, NULL);
518644267aSskrll
52646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
538644267aSskrll /* compatible imxtype */
54646c0f59Sthorpej { .compat = "fsl,imx6q-fec", .value = 6 },
55646c0f59Sthorpej { .compat = "fsl,imx6sx-fec", .value = 7 },
56ec189949Sthorpej DEVICE_COMPAT_EOL
578644267aSskrll };
588644267aSskrll
598644267aSskrll static int enet_init_clocks(struct enet_softc *);
608644267aSskrll static void enet_phy_reset(struct enet_fdt_softc *, const int);
618644267aSskrll static int enet_phy_id(struct enet_softc *, const int);
628644267aSskrll static void *enet_intr_establish(struct enet_softc *, int, u_int);
638644267aSskrll
648644267aSskrll int
enet_match(device_t parent,cfdata_t cf,void * aux)658644267aSskrll enet_match(device_t parent, cfdata_t cf, void *aux)
668644267aSskrll {
678644267aSskrll struct fdt_attach_args * const faa = aux;
688644267aSskrll
696e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
708644267aSskrll }
718644267aSskrll
728644267aSskrll void
enet_attach(device_t parent,device_t self,void * aux)738644267aSskrll enet_attach(device_t parent, device_t self, void *aux)
748644267aSskrll {
758644267aSskrll struct enet_fdt_softc * const efsc = device_private(self);
768644267aSskrll struct enet_softc *sc = &efsc->sc_enet;
778644267aSskrll struct fdt_attach_args * const faa = aux;
788644267aSskrll prop_dictionary_t prop = device_properties(self);
798644267aSskrll const int phandle = faa->faa_phandle;
808644267aSskrll bus_space_tag_t bst = faa->faa_bst;
818644267aSskrll bus_space_handle_t bsh;
828644267aSskrll bus_addr_t addr;
838644267aSskrll bus_size_t size;
848644267aSskrll int error;
858644267aSskrll
868644267aSskrll if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
878644267aSskrll aprint_error(": couldn't get enet registers\n");
888644267aSskrll return;
898644267aSskrll }
908644267aSskrll
918644267aSskrll error = bus_space_map(bst, addr, size, 0, &bsh);
928644267aSskrll if (error) {
938644267aSskrll aprint_error(": couldn't map enet registers: %d\n", error);
948644267aSskrll return;
958644267aSskrll }
968644267aSskrll
978644267aSskrll sc->sc_clk_ipg = fdtbus_clock_get(phandle, "ipg");
988644267aSskrll if (sc->sc_clk_ipg == NULL) {
998644267aSskrll aprint_error(": couldn't get clock ipg\n");
1008644267aSskrll goto failure;
1018644267aSskrll }
1028644267aSskrll sc->sc_clk_enet = fdtbus_clock_get(phandle, "ahb");
1038644267aSskrll if (sc->sc_clk_enet == NULL) {
1048644267aSskrll aprint_error(": couldn't get clock ahb\n");
1058644267aSskrll goto failure;
1068644267aSskrll }
1078644267aSskrll sc->sc_clk_enet_ref = fdtbus_clock_get(phandle, "ptp");
1088644267aSskrll if (sc->sc_clk_enet_ref == NULL) {
1098644267aSskrll aprint_error(": couldn't get clock ptp\n");
1108644267aSskrll goto failure;
1118644267aSskrll }
1128644267aSskrll
1138644267aSskrll if (fdtbus_clock_enable(phandle, "enet_clk_ref", false) != 0) {
1148644267aSskrll aprint_error(": couldn't enable clock enet_clk_ref\n");
1158644267aSskrll goto failure;
1168644267aSskrll }
1178644267aSskrll if (fdtbus_clock_enable(phandle, "enet_out", false) != 0) {
1188644267aSskrll aprint_error(": couldn't enable clock enet_out\n");
1198644267aSskrll goto failure;
1208644267aSskrll }
1218644267aSskrll
1228644267aSskrll aprint_naive("\n");
1238644267aSskrll aprint_normal(": Gigabit Ethernet Controller\n");
1248644267aSskrll
1258644267aSskrll sc->sc_dev = self;
1268644267aSskrll sc->sc_iot = bst;
1278644267aSskrll sc->sc_ioh = bsh;
1288644267aSskrll sc->sc_dmat = faa->faa_dmat;
1298644267aSskrll
1306e54367aSthorpej sc->sc_imxtype = of_compatible_lookup(phandle, compat_data)->value;
1318644267aSskrll sc->sc_unit = 0;
1328644267aSskrll sc->sc_phyid = enet_phy_id(sc, phandle);
1338644267aSskrll
1348644267aSskrll const char *phy_mode = fdtbus_get_string(phandle, "phy-mode");
1358644267aSskrll if (phy_mode == NULL) {
1368644267aSskrll aprint_error(": missing 'phy-mode' property\n");
1378644267aSskrll goto failure;
1388644267aSskrll }
1398644267aSskrll
1408644267aSskrll if (strcmp(phy_mode, "rgmii-txid") == 0) {
1418644267aSskrll prop_dictionary_set_bool(prop, "tx_internal_delay", true);
1428644267aSskrll sc->sc_rgmii = 1;
1438644267aSskrll } else if (strcmp(phy_mode, "rgmii-rxid") == 0) {
1448644267aSskrll prop_dictionary_set_bool(prop, "rx_internal_delay", true);
1458644267aSskrll sc->sc_rgmii = 1;
1468644267aSskrll } else if (strcmp(phy_mode, "rgmii-id") == 0) {
1478644267aSskrll prop_dictionary_set_bool(prop, "tx_internal_delay", true);
1488644267aSskrll prop_dictionary_set_bool(prop, "rx_internal_delay", true);
1498644267aSskrll sc->sc_rgmii = 1;
1508644267aSskrll } else if (strcmp(phy_mode, "rgmii") == 0) {
1518644267aSskrll sc->sc_rgmii = 1;
1528644267aSskrll } else {
1538644267aSskrll sc->sc_rgmii = 0;
1548644267aSskrll }
1558644267aSskrll
1568644267aSskrll sc->sc_ih = enet_intr_establish(sc, phandle, 0);
1578644267aSskrll if (sc->sc_ih == NULL)
1588644267aSskrll goto failure;
1598644267aSskrll
1608644267aSskrll if (sc->sc_imxtype == 7) {
1618644267aSskrll sc->sc_ih2 = enet_intr_establish(sc, phandle, 1);
1628644267aSskrll sc->sc_ih3 = enet_intr_establish(sc, phandle, 2);
1638644267aSskrll if (sc->sc_ih2 == NULL || sc->sc_ih3 == NULL)
1648644267aSskrll goto failure;
1658644267aSskrll }
1668644267aSskrll
1678644267aSskrll enet_init_clocks(sc);
1688644267aSskrll sc->sc_clock = clk_get_rate(sc->sc_clk_ipg);
1698644267aSskrll
1708644267aSskrll enet_phy_reset(efsc, phandle);
1718644267aSskrll
1728644267aSskrll if (enet_attach_common(self) != 0)
1738644267aSskrll goto failure;
1748644267aSskrll
1758644267aSskrll return;
1768644267aSskrll
1778644267aSskrll failure:
178*ac5c9969Smrg bus_space_unmap(bst, bsh, size);
1798644267aSskrll return;
1808644267aSskrll }
1818644267aSskrll
1828644267aSskrll static void *
enet_intr_establish(struct enet_softc * sc,int phandle,u_int index)1838644267aSskrll enet_intr_establish(struct enet_softc *sc, int phandle, u_int index)
1848644267aSskrll {
1858644267aSskrll char intrstr[128];
18682b8374aSjmcneill char xname[16];
1878644267aSskrll void *ih;
1888644267aSskrll
1898644267aSskrll if (!fdtbus_intr_str(phandle, index, intrstr, sizeof(intrstr))) {
1908644267aSskrll aprint_error_dev(sc->sc_dev, "failed to decode interrupt %d\n",
1918644267aSskrll index);
1928644267aSskrll return NULL;
1938644267aSskrll }
1948644267aSskrll
19582b8374aSjmcneill snprintf(xname, sizeof(xname), "%s #%u", device_xname(sc->sc_dev),
19682b8374aSjmcneill index);
19782b8374aSjmcneill ih = fdtbus_intr_establish_xname(phandle, index, IPL_NET, 0,
19882b8374aSjmcneill enet_intr, sc, xname);
1998644267aSskrll if (ih == NULL) {
2008644267aSskrll aprint_error_dev(sc->sc_dev, "failed to establish interrupt on %s\n",
2018644267aSskrll intrstr);
2028644267aSskrll return NULL;
2038644267aSskrll }
2048644267aSskrll aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
2058644267aSskrll
2068644267aSskrll return ih;
2078644267aSskrll }
2088644267aSskrll
2098644267aSskrll static int
enet_init_clocks(struct enet_softc * sc)2108644267aSskrll enet_init_clocks(struct enet_softc *sc)
2118644267aSskrll {
2128644267aSskrll int error;
2138644267aSskrll
2148644267aSskrll error = clk_enable(sc->sc_clk_ipg);
2158644267aSskrll if (error) {
2168644267aSskrll aprint_error_dev(sc->sc_dev, "couldn't enable ipg: %d\n", error);
2178644267aSskrll return error;
2188644267aSskrll }
2198644267aSskrll error = clk_enable(sc->sc_clk_enet);
2208644267aSskrll if (error) {
2218644267aSskrll aprint_error_dev(sc->sc_dev, "couldn't enable enet: %d\n", error);
2228644267aSskrll return error;
2238644267aSskrll }
2248644267aSskrll error = clk_enable(sc->sc_clk_enet_ref);
2258644267aSskrll if (error) {
2268644267aSskrll aprint_error_dev(sc->sc_dev, "couldn't enable enet_ref: %d\n", error);
2278644267aSskrll return error;
2288644267aSskrll }
2298644267aSskrll
2308644267aSskrll return 0;
2318644267aSskrll }
2328644267aSskrll
2338644267aSskrll static void
enet_phy_reset(struct enet_fdt_softc * sc,const int phandle)2348644267aSskrll enet_phy_reset(struct enet_fdt_softc *sc, const int phandle)
2358644267aSskrll {
2368644267aSskrll u_int msec;
2378644267aSskrll
2388644267aSskrll sc->sc_pin_reset = fdtbus_gpio_acquire(phandle, "phy-reset-gpios", GPIO_PIN_OUTPUT);
2398644267aSskrll if (sc->sc_pin_reset == NULL) {
2408644267aSskrll aprint_error_dev(sc->sc_enet.sc_dev, "couldn't find phy reset gpios\n");
2418644267aSskrll return;
2428644267aSskrll }
2438644267aSskrll
2448644267aSskrll if (of_getprop_uint32(phandle, "phy-reset-duration", &msec))
2458644267aSskrll msec = 1;
2468644267aSskrll
2478644267aSskrll /* Reset */
2488644267aSskrll fdtbus_gpio_write(sc->sc_pin_reset, 1);
2498644267aSskrll delay(msec * 1000);
2508644267aSskrll fdtbus_gpio_write(sc->sc_pin_reset, 0);
2518644267aSskrll
2528644267aSskrll /* Post delay */
2538644267aSskrll if (of_getprop_uint32(phandle, "phy-reset-post-delay", &msec))
2548644267aSskrll msec = 0;
2558644267aSskrll
2568644267aSskrll delay(msec * 1000);
2578644267aSskrll }
2588644267aSskrll
2598644267aSskrll static int
enet_phy_id(struct enet_softc * sc,const int phandle)2608644267aSskrll enet_phy_id(struct enet_softc *sc, const int phandle)
2618644267aSskrll {
2628644267aSskrll int phy_phandle;
2638644267aSskrll bus_addr_t addr;
2648644267aSskrll
2658644267aSskrll phy_phandle = fdtbus_get_phandle(phandle, "phy-handle");
2668644267aSskrll if (phy_phandle == -1)
2678644267aSskrll return MII_PHY_ANY;
2688644267aSskrll
2698644267aSskrll if (fdtbus_get_reg(phy_phandle, 0, &addr, NULL) != 0)
2708644267aSskrll return MII_PHY_ANY;
2718644267aSskrll
2728644267aSskrll return (int)addr;
2738644267aSskrll }
274