xref: /freebsd/sys/dev/dwc/dwc4_core.c (revision c4fde87a3236b500246d4fce76dbca2a3d64f73a)
1 /*-
2  * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/bus.h>
29 #include <sys/kernel.h>
30 #include <sys/lock.h>
31 #include <sys/malloc.h>
32 #include <sys/mbuf.h>
33 #include <sys/module.h>
34 #include <sys/mutex.h>
35 #include <sys/rman.h>
36 #include <sys/socket.h>
37 
38 #include <net/if.h>
39 #include <net/ethernet.h>
40 #include <net/if_dl.h>
41 #include <net/if_media.h>
42 
43 #include <machine/bus.h>
44 
45 #include <dev/clk/clk.h>
46 #include <dev/hwreset/hwreset.h>
47 
48 #include <dev/mii/mii.h>
49 #include <dev/mii/miivar.h>
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52 #include <dev/mii/mii_fdt.h>
53 
54 #include <dev/dwc/if_dwcvar.h>
55 #include <dev/dwc/dwc4_reg.h>
56 #include <dev/dwc/dwc4_dma.h>
57 
58 #include "if_dwc_if.h"
59 
60 struct dwc_hash_maddr_ctx {
61 	struct dwc_softc *sc;
62 	uint32_t hash[8];
63 };
64 
65 #define	STATS_HARVEST_INTERVAL	2
66 
67 /* Pause time field in the transmitted control frame */
68 static int dwc_pause_time = 0xffff;
69 TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time);
70 
71 /*
72  * MIIBUS functions
73  */
74 
75 int
76 dwc4_miibus_read_reg(device_t dev, int phy, int reg)
77 {
78 	struct dwc_softc *sc;
79 	uint32_t mii;
80 	size_t cnt;
81 	int rv = 0;
82 
83 	sc = device_get_softc(dev);
84 
85 	mii = ((phy << MACMDIOAR_PA_S) & MACMDIOAR_PA_M)
86 	    | ((reg << MACMDIOAR_RDA_S) & MACMDIOAR_RDA_M)
87 	    | (sc->mii_clk << MACMDIOAR_CR_S)
88 	    | MACMDIOAR_READ
89 	    | MACMDIOAR_GB; /* Busy flag */
90 
91 	WRITE4(sc, ETH_MACMDIOAR, mii);
92 
93 	for (cnt = 0; cnt < 1000; cnt++) {
94 		if (!(READ4(sc, ETH_MACMDIOAR) & MACMDIOAR_GB)) {
95 			rv = READ4(sc, ETH_MACMDIODR);
96 			break;
97 		}
98 		DELAY(10);
99 	}
100 
101 	return rv;
102 }
103 
104 int
105 dwc4_miibus_write_reg(device_t dev, int phy, int reg, int val)
106 {
107 	struct dwc_softc *sc;
108 	uint32_t mii;
109 	size_t cnt;
110 
111 	sc = device_get_softc(dev);
112 
113 	mii = ((phy << MACMDIOAR_PA_S) & MACMDIOAR_PA_M)
114 	    | ((reg << MACMDIOAR_RDA_S) & MACMDIOAR_RDA_M)
115 	    | (sc->mii_clk << MACMDIOAR_CR_S)
116 	    | MACMDIOAR_WRITE
117 	    | MACMDIOAR_GB; /* Busy flag */
118 
119 	WRITE4(sc, ETH_MACMDIODR, val);
120 	WRITE4(sc, ETH_MACMDIOAR, mii);
121 
122 	for (cnt = 0; cnt < 1000; cnt++) {
123 		if (!(READ4(sc, ETH_MACMDIOAR) & MACMDIOAR_GB)) {
124 			break;
125                 }
126 		DELAY(10);
127 	}
128 
129 	return (0);
130 }
131 
132 void
133 dwc4_miibus_statchg(device_t dev)
134 {
135 	struct dwc_softc *sc;
136 	struct mii_data *mii;
137 	uint32_t reg;
138 
139 	/*
140 	 * Called by the MII bus driver when the PHY establishes
141 	 * link to set the MAC interface registers.
142 	 */
143 
144 	sc = device_get_softc(dev);
145 
146 	DWC_ASSERT_LOCKED(sc);
147 
148 	mii = sc->mii_softc;
149 
150 	if (mii->mii_media_status & IFM_ACTIVE)
151 		sc->link_is_up = true;
152 	else
153 		sc->link_is_up = false;
154 
155 	reg = READ4(sc, ETH_MACCR);
156 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
157 	case IFM_1000_T:
158 	case IFM_1000_SX:
159 		reg &= ~(MACCR_FES | MACCR_PS);
160 		break;
161 	case IFM_100_TX:
162 		reg |= (MACCR_FES | MACCR_PS);
163 		break;
164 	case IFM_10_T:
165 		reg &= ~(MACCR_FES);
166 		reg |= (MACCR_PS);
167 		break;
168 	case IFM_NONE:
169 		sc->link_is_up = false;
170 		return;
171 	default:
172 		sc->link_is_up = false;
173 		device_printf(dev, "Unsupported media %u\n",
174 		    IFM_SUBTYPE(mii->mii_media_active));
175 		return;
176 	}
177 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
178 		reg |= (MACCR_DM);
179 	else
180 		reg &= ~(MACCR_DM);
181 	WRITE4(sc, ETH_MACCR, reg);
182 
183 	IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
184 
185 }
186 
187 void
188 dwc4_core_setup(struct dwc_softc *sc)
189 {
190 	uint32_t reg;
191 
192 	DWC_ASSERT_LOCKED(sc);
193 
194 	reg = READ4(sc, ETH_MACVR);
195 	device_printf(sc->dev, "MAC Version: %x\n", reg);
196 
197 	/* Enable core */
198 	reg = READ4(sc, ETH_MACCR);
199 	reg |= (MACCR_JD | MACCR_ACS | MACCR_BE);
200 	WRITE4(sc, ETH_MACCR, reg);
201 }
202 
203 void
204 dwc4_enable_mac(struct dwc_softc *sc, bool enable)
205 {
206 	uint32_t reg;
207 
208 	DWC_ASSERT_LOCKED(sc);
209 
210 	reg = READ4(sc, ETH_MACCR);
211 	if (enable)
212 		reg |= MACCR_TE | MACCR_RE;
213 	else
214 		reg &= ~(MACCR_TE | MACCR_RE);
215 	WRITE4(sc, ETH_MACCR, reg);
216 }
217 
218 void
219 dwc4_enable_csum_offload(struct dwc_softc *sc)
220 {
221 	uint32_t reg;
222 
223 	DWC_ASSERT_LOCKED(sc);
224 	reg = READ4(sc, ETH_MACCR);
225 	if ((if_getcapenable(sc->ifp) & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) !=
226 	    0)
227 		reg |= MACCR_IPC;
228 	else
229 		reg &= ~MACCR_IPC;
230 	WRITE4(sc, ETH_MACCR, reg);
231 }
232 
233 static const uint8_t nibbletab[] = {
234 	/* 0x0 0000 -> 0000 */  0x0,
235 	/* 0x1 0001 -> 1000 */  0x8,
236 	/* 0x2 0010 -> 0100 */  0x4,
237 	/* 0x3 0011 -> 1100 */  0xc,
238 	/* 0x4 0100 -> 0010 */  0x2,
239 	/* 0x5 0101 -> 1010 */  0xa,
240 	/* 0x6 0110 -> 0110 */  0x6,
241 	/* 0x7 0111 -> 1110 */  0xe,
242 	/* 0x8 1000 -> 0001 */  0x1,
243 	/* 0x9 1001 -> 1001 */  0x9,
244 	/* 0xa 1010 -> 0101 */  0x5,
245 	/* 0xb 1011 -> 1101 */  0xd,
246 	/* 0xc 1100 -> 0011 */  0x3,
247 	/* 0xd 1101 -> 1011 */  0xb,
248 	/* 0xe 1110 -> 0111 */  0x7,
249 	/* 0xf 1111 -> 1111 */  0xf, };
250 
251 static uint8_t
252 bitreverse(uint8_t x)
253 {
254 
255 	return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4];
256 }
257 
258 static u_int
259 dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
260 {
261 	struct dwc_hash_maddr_ctx *ctx = arg;
262 	uint32_t crc, hashbit, hashreg;
263 	uint8_t val;
264 
265 	crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN);
266 	/* Take lower 8 bits and reverse it */
267 	val = bitreverse(~crc & 0xff);
268 	/*
269 	 * TODO: There is probably a HW_FEATURES bit which isn't
270 	 * related to the extended descriptors that describe this
271 	 */
272 	if (!ctx->sc->dma_ext_desc)
273 		val >>= 2; /* Only need lower 6 bits */
274 	hashreg = (val >> 5);
275 	hashbit = (val & 31);
276 	ctx->hash[hashreg] |= (1 << hashbit);
277 
278 	return (1);
279 }
280 
281 void
282 dwc4_setup_rxfilter(struct dwc_softc *sc)
283 {
284 	struct dwc_hash_maddr_ctx ctx;
285 	if_t ifp;
286 	uint8_t *eaddr;
287 	uint32_t ffval, hi, lo;
288 	int nhash, i;
289 
290 	DWC_ASSERT_LOCKED(sc);
291 
292 	ifp = sc->ifp;
293 	/*
294 	 * TODO: There is probably a HW_FEATURES bit which isn't
295 	 * related to the extended descriptors that describe this
296 	 */
297 	nhash = sc->dma_ext_desc == false ? 2 : 8;
298 
299 	/*
300 	 * Set the multicast (group) filter hash.
301 	 */
302 	if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
303 		ffval = (MACPFR_PM);
304 		for (i = 0; i < nhash; i++)
305 			ctx.hash[i] = ~0;
306 	} else {
307 		ffval = (MACPFR_HMC);
308 		for (i = 0; i < nhash; i++)
309 			ctx.hash[i] = 0;
310 		ctx.sc = sc;
311 		if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx);
312 	}
313 
314 	/*
315 	 * Set the individual address filter hash.
316 	 */
317 	if ((if_getflags(ifp) & IFF_PROMISC) != 0)
318 		ffval |= MACPFR_PR;
319 
320 	/*
321 	 * Set the primary address.
322 	 */
323 	eaddr = if_getlladdr(ifp);
324 	lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
325 	    (eaddr[3] << 24);
326 	hi = eaddr[4] | (eaddr[5] << 8);
327 	WRITE4(sc, ETH_MACA0LR, lo);
328 	WRITE4(sc, ETH_MACA0HR, hi);
329 
330 	WRITE4(sc, ETH_MACPFR, ffval);
331 	if (!sc->dma_ext_desc) {
332 		WRITE4(sc, ETH_MACHT0R, ctx.hash[0]);
333 		WRITE4(sc, ETH_MACHT1R, ctx.hash[1]);
334 	} else
335 		panic("implement me: ext_desc");
336 }
337 
338 void
339 dwc4_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
340 {
341 	uint32_t hi, lo, rnd;
342 
343 	/*
344 	 * Try to recover a MAC address from the running hardware. If there's
345 	 * something non-zero there, assume the bootloader did the right thing
346 	 * and just use it.
347 	 *
348 	 * Otherwise, set the address to a convenient locally assigned address,
349 	 * 'bsd' + random 24 low-order bits.  'b' is 0x62, which has the locally
350 	 * assigned bit set, and the broadcast/multicast bit clear.
351 	 */
352 	lo = READ4(sc, ETH_MACA0LR);
353 	hi = READ4(sc, ETH_MACA0HR) & 0xffff;
354 	if ((lo != 0xffffffff) || (hi != 0xffff)) {
355 		hwaddr[0] = (lo >>  0) & 0xff;
356 		hwaddr[1] = (lo >>  8) & 0xff;
357 		hwaddr[2] = (lo >> 16) & 0xff;
358 		hwaddr[3] = (lo >> 24) & 0xff;
359 		hwaddr[4] = (hi >>  0) & 0xff;
360 		hwaddr[5] = (hi >>  8) & 0xff;
361 	} else {
362 		rnd = arc4random() & 0x00ffffff;
363 		hwaddr[0] = 'b';
364 		hwaddr[1] = 's';
365 		hwaddr[2] = 'd';
366 		hwaddr[3] = rnd >> 16;
367 		hwaddr[4] = rnd >>  8;
368 		hwaddr[5] = rnd >>  0;
369 	}
370 }
371 
372 /*
373  * Stats
374  */
375 
376 static void
377 dwc4_clear_stats(struct dwc_softc *sc)
378 {
379 	uint32_t reg;
380 
381 	reg = READ4(sc, ETH_MMC_CONTROL);
382 	reg |= MMC_CONTROL_CNTRST;
383 	WRITE4(sc, ETH_MMC_CONTROL, reg);
384 }
385 
386 void
387 dwc4_harvest_stats(struct dwc_softc *sc)
388 {
389 	if_t ifp __unused;
390 	int val;
391 
392 	/* We don't need to harvest too often. */
393 	if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL)
394 		return;
395 
396 	sc->stats_harvest_count = 0;
397 	ifp = sc->ifp;
398 
399 	val = READ4(sc, ETH_RX_RECEIVE_ERROR);
400 	if_inc_counter(ifp, IFCOUNTER_IERRORS, val);
401 
402 	val = READ4(sc, ETH_TX_PACKET_COUNT_GOOD_BAD);
403 	val -= READ4(sc, ETH_TX_PACKET_COUNT_GOOD);
404 	if_inc_counter(ifp, IFCOUNTER_OERRORS, val);
405 
406 	val = READ4(sc, ETH_TX_SINGLE_COLLISION_GOOD_PACKETS);
407 	val += READ4(sc, ETH_TX_MULTIPLE_COLLISION_GOOD_PACKETS);
408 	val += READ4(sc, ETH_TX_LATE_COLLISION_PACKETS);
409 	val += READ4(sc, ETH_TX_EXCESSIVE_COLLISION_PACKETS);
410 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val);
411 
412 	dwc4_clear_stats(sc);
413 }
414 
415 void
416 dwc4_intr(struct dwc_softc *sc)
417 {
418 	uint32_t reg;
419 
420 	DWC_ASSERT_LOCKED(sc);
421 
422 	reg = READ4(sc, ETH_MACISR);
423 	if (reg)
424 		READ4(sc, ETH_MACPHYCSR);
425 }
426 
427 void
428 dwc4_intr_disable(struct dwc_softc *sc)
429 {
430 
431 	WRITE4(sc, ETH_MACIER, 0);
432 }
433