xref: /netbsd-src/sys/arch/arm/nxp/if_enet_imx.c (revision ac5c9969631f2d57746be735ac3d2d2f71afb7cc)
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