1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/bus.h>
30 #include <sys/errno.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/module.h>
34 #include <sys/socket.h>
35 #include <sys/sockio.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38 
39 #include <net/if.h>
40 #include <net/if_var.h>
41 #include <net/if_arp.h>
42 #include <net/ethernet.h>
43 #include <net/if_dl.h>
44 #include <net/if_media.h>
45 #include <net/if_types.h>
46 
47 #include <machine/bus.h>
48 #include <dev/iicbus/iic.h>
49 #include <dev/iicbus/iiconf.h>
50 #include <dev/iicbus/iicbus.h>
51 #include <dev/mii/mii.h>
52 #include <dev/mii/miivar.h>
53 #include <dev/mdio/mdio.h>
54 #include <dev/extres/clk/clk.h>
55 #include <dev/extres/hwreset/hwreset.h>
56 
57 #include <dev/fdt/fdt_common.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/ofw_bus_subr.h>
60 
61 #include <dev/etherswitch/etherswitch.h>
62 
63 #include <dev/etherswitch/ar40xx/ar40xx_var.h>
64 #include <dev/etherswitch/ar40xx/ar40xx_reg.h>
65 #include <dev/etherswitch/ar40xx/ar40xx_hw.h>
66 #include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
67 #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
68 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
69 #include <dev/etherswitch/ar40xx/ar40xx_phy.h>
70 #include <dev/etherswitch/ar40xx/ar40xx_debug.h>
71 
72 #include "mdio_if.h"
73 #include "miibus_if.h"
74 #include "etherswitch_if.h"
75 
76 
77 int
78 ar40xx_phy_tick(struct ar40xx_softc *sc)
79 {
80 	struct mii_softc *miisc;
81 	struct mii_data *mii;
82 	int phy;
83 	uint32_t reg;
84 
85 	AR40XX_LOCK_ASSERT(sc);
86 
87 	AR40XX_REG_BARRIER_READ(sc);
88 	/*
89 	 * Loop over; update phy port status here
90 	 */
91 	for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
92 		/*
93 		 * Port here is PHY, not port!
94 		 */
95 		reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(phy + 1));
96 
97 		mii = device_get_softc(sc->sc_phys.miibus[phy]);
98 
99 		/*
100 		 * Compare the current link status to the previous link
101 		 * status.  We may need to clear ATU / change phy config.
102 		 */
103 		if (((reg & AR40XX_PORT_STATUS_LINK_UP) != 0) &&
104 		    (mii->mii_media_status & IFM_ACTIVE) == 0) {
105 			AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS,
106 			    "%s: PHY %d: down -> up\n", __func__, phy);
107 			ar40xx_hw_port_link_up(sc, phy + 1);
108 			ar40xx_hw_atu_flush_port(sc, phy + 1);
109 		}
110 		if (((reg & AR40XX_PORT_STATUS_LINK_UP) == 0) &&
111 		    (mii->mii_media_status & IFM_ACTIVE) != 0) {
112 			AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS,
113 			    "%s: PHY %d: up -> down\n", __func__, phy);
114 			ar40xx_hw_port_link_down(sc, phy + 1);
115 			ar40xx_hw_atu_flush_port(sc, phy + 1);
116 		}
117 
118 		mii_tick(mii);
119 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
120 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
121 			    miisc->mii_inst)
122 				continue;
123 			ukphy_status(miisc);
124 			mii_phy_update(miisc, MII_POLLSTAT);
125 		}
126 	}
127 
128 	return (0);
129 }
130 
131 static inline int
132 ar40xx_portforphy(int phy)
133 {
134 
135 	return (phy+1);
136 }
137 
138 struct mii_data *
139 ar40xx_phy_miiforport(struct ar40xx_softc *sc, int port)
140 {
141 	int phy;
142 
143 	phy = port-1;
144 
145 	if (phy < 0 || phy >= AR40XX_NUM_PHYS)
146 		return (NULL);
147 	return (device_get_softc(sc->sc_phys.miibus[phy]));
148 }
149 
150 if_t
151 ar40xx_phy_ifpforport(struct ar40xx_softc *sc, int port)
152 {
153 	int phy;
154 
155 	phy = port-1;
156 	if (phy < 0 || phy >= AR40XX_NUM_PHYS)
157 		return (NULL);
158 	return (sc->sc_phys.ifp[phy]);
159 }
160 
161 static int
162 ar40xx_ifmedia_upd(if_t ifp)
163 {
164 	struct ar40xx_softc *sc = if_getsoftc(ifp);
165 	struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp));
166 
167 	AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
168 	    __func__, if_getdunit(ifp));
169 
170 	if (mii == NULL)
171 		return (ENXIO);
172 	mii_mediachg(mii);
173 	return (0);
174 }
175 
176 static void
177 ar40xx_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
178 {
179 	struct ar40xx_softc *sc = if_getsoftc(ifp);
180 	struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp));
181 
182 	AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n",
183 	    __func__, if_getdunit(ifp));
184 
185 	if (mii == NULL)
186 		return;
187 	mii_pollstat(mii);
188 
189 	ifmr->ifm_active = mii->mii_media_active;
190 	ifmr->ifm_status = mii->mii_media_status;
191 }
192 
193 int
194 ar40xx_attach_phys(struct ar40xx_softc *sc)
195 {
196 	int phy, err = 0;
197 	char name[IFNAMSIZ];
198 
199 	/* PHYs need an interface, so we generate a dummy one */
200 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
201 	for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
202 		sc->sc_phys.ifp[phy] = if_alloc(IFT_ETHER);
203 		if (sc->sc_phys.ifp[phy] == NULL) {
204 			device_printf(sc->sc_dev,
205 			    "PHY %d: couldn't allocate ifnet structure\n",
206 			    phy);
207 			err = ENOMEM;
208 			break;
209 		}
210 
211 		sc->sc_phys.ifp[phy]->if_softc = sc;
212 		sc->sc_phys.ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
213 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
214 		sc->sc_phys.ifname[phy] = malloc(strlen(name)+1, M_DEVBUF,
215 		    M_WAITOK);
216 		bcopy(name, sc->sc_phys.ifname[phy], strlen(name)+1);
217 		if_initname(sc->sc_phys.ifp[phy], sc->sc_phys.ifname[phy],
218 		    ar40xx_portforphy(phy));
219 		err = mii_attach(sc->sc_dev, &sc->sc_phys.miibus[phy],
220 		    sc->sc_phys.ifp[phy], ar40xx_ifmedia_upd,
221 		    ar40xx_ifmedia_sts, BMSR_DEFCAPMASK,
222 		    phy, MII_OFFSET_ANY, 0);
223 		device_printf(sc->sc_dev,
224 		    "%s attached to pseudo interface %s\n",
225 		    device_get_nameunit(sc->sc_phys.miibus[phy]),
226 		    sc->sc_phys.ifp[phy]->if_xname);
227 		if (err != 0) {
228 			device_printf(sc->sc_dev,
229 			    "attaching PHY %d failed\n",
230 			    phy);
231 			return (err);
232 		}
233 	}
234 	return (0);
235 }
236 
237 int
238 ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc)
239 {
240 	int phy;
241 	uint32_t id1, id2;
242 
243 	for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) {
244 		id1 = MDIO_READREG(sc->sc_mdio_dev, phy, 2);
245 		id2 = MDIO_READREG(sc->sc_mdio_dev, phy, 3);
246 		device_printf(sc->sc_dev,
247 		    "%s: PHY %d: ID1=0x%04x, ID2=0x%04x\n",
248 		    __func__, phy, id1, id2);
249 	}
250 
251 	return (0);
252 }
253