xref: /freebsd/sys/dev/qcom_ess_edma/qcom_ess_edma_gmac.c (revision 9f32893b05dabedc7f8332ec12e2a944b6543158)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021 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 unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/module.h>
39 #include <sys/rman.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/mutex.h>
43 #include <sys/gpio.h>
44 #include <sys/mbuf.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #include <sys/smp.h>
48 #include <sys/proc.h>
49 #include <sys/sched.h>
50 #include <sys/queue.h>
51 #include <sys/taskqueue.h>
52 
53 #include <net/if.h>
54 #include <net/if_var.h>
55 #include <net/if_vlan_var.h>
56 #include <net/if_media.h>
57 #include <net/ethernet.h>
58 #include <net/if_types.h>
59 
60 #include <machine/bus.h>
61 #include <machine/resource.h>
62 #include <machine/atomic.h>
63 
64 #include <dev/gpio/gpiobusvar.h>
65 
66 #include <dev/fdt/fdt_common.h>
67 #include <dev/ofw/ofw_bus.h>
68 #include <dev/ofw/ofw_bus_subr.h>
69 
70 #include <dev/qcom_ess_edma/qcom_ess_edma_var.h>
71 #include <dev/qcom_ess_edma/qcom_ess_edma_reg.h>
72 #include <dev/qcom_ess_edma/qcom_ess_edma_hw.h>
73 #include <dev/qcom_ess_edma/qcom_ess_edma_desc.h>
74 #include <dev/qcom_ess_edma/qcom_ess_edma_rx.h>
75 #include <dev/qcom_ess_edma/qcom_ess_edma_tx.h>
76 #include <dev/qcom_ess_edma/qcom_ess_edma_debug.h>
77 #include <dev/qcom_ess_edma/qcom_ess_edma_gmac.h>
78 
79 static int
qcom_ess_edma_gmac_mediachange(if_t ifp)80 qcom_ess_edma_gmac_mediachange(if_t ifp)
81 {
82 	struct qcom_ess_edma_gmac *gmac = if_getsoftc(ifp);
83 	struct qcom_ess_edma_softc *sc = gmac->sc;
84 	struct ifmedia *ifm = &gmac->ifm;
85 	struct ifmedia_entry *ife = ifm->ifm_cur;
86 
87 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
88 	return (EINVAL);
89 
90 	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
91 		device_printf(sc->sc_dev,
92 		    "AUTO is not supported this MAC");
93 		return (EINVAL);
94 	}
95 
96 	/*
97 	 * Ignore everything
98 	 */
99 	return (0);
100 }
101 
102 static void
qcom_ess_edma_gmac_mediastatus(if_t ifp,struct ifmediareq * ifmr)103 qcom_ess_edma_gmac_mediastatus(if_t ifp, struct ifmediareq *ifmr)
104 {
105 
106 	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
107 	ifmr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX;
108 }
109 
110 static int
qcom_ess_edma_gmac_ioctl(if_t ifp,u_long command,caddr_t data)111 qcom_ess_edma_gmac_ioctl(if_t ifp, u_long command, caddr_t data)
112 {
113 	struct qcom_ess_edma_gmac *gmac = if_getsoftc(ifp);
114 	struct qcom_ess_edma_softc *sc = gmac->sc;
115 	struct ifreq *ifr = (struct ifreq *) data;
116 	int error, mask;
117 
118 	switch (command) {
119 	case SIOCSIFFLAGS:
120 		if ((if_getflags(ifp) & IFF_UP) != 0) {
121 			/* up */
122 			QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_STATE,
123 			    "%s: gmac%d: IFF_UP\n",
124 			    __func__,
125 			    gmac->id);
126 			if_setdrvflagbits(ifp, IFF_DRV_RUNNING,
127 			    IFF_DRV_OACTIVE);
128 			if_link_state_change(ifp, LINK_STATE_UP);
129 
130 		} else if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
131 			/* down */
132 			if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
133 			QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_STATE,
134 			    "%s: gmac%d: IF down\n",
135 			    __func__,
136 			    gmac->id);
137 			if_link_state_change(ifp, LINK_STATE_DOWN);
138 		}
139 		error = 0;
140 		break;
141 	case SIOCGIFMEDIA:
142 	case SIOCSIFMEDIA:
143 		error = ifmedia_ioctl(ifp, ifr, &gmac->ifm, command);
144 		break;
145 	case SIOCSIFCAP:
146 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
147 		error = 0;
148 
149 		if ((mask & IFCAP_RXCSUM) != 0 &&
150 		    (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0)
151 			if_togglecapenable(ifp, IFCAP_RXCSUM);
152 
153 		if ((mask & IFCAP_VLAN_HWTAGGING) != 0 &&
154 		    (if_getcapabilities(ifp) & IFCAP_VLAN_HWTAGGING) != 0)
155 			if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
156 
157 		VLAN_CAPABILITIES(ifp);
158 		break;
159 	default:
160 		error = ether_ioctl(ifp, command, data);
161 		break;
162 	}
163 
164 	return (error);
165 }
166 
167 static void
qcom_ess_edma_gmac_init(void * arg)168 qcom_ess_edma_gmac_init(void *arg)
169 {
170 	struct qcom_ess_edma_gmac *gmac = arg;
171 	struct qcom_ess_edma_softc *sc = gmac->sc;
172 
173 	QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_STATE,
174 	    "%s: gmac%d: called\n",
175 	    __func__,
176 	    gmac->id);
177 
178 	if_setdrvflagbits(gmac->ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
179 	if_link_state_change(gmac->ifp, LINK_STATE_UP);
180 }
181 
182 static int
qcom_ess_edma_gmac_transmit(if_t ifp,struct mbuf * m)183 qcom_ess_edma_gmac_transmit(if_t ifp, struct mbuf *m)
184 {
185 	struct qcom_ess_edma_gmac *gmac = if_getsoftc(ifp);
186 	struct qcom_ess_edma_softc *sc = gmac->sc;
187 	struct qcom_ess_edma_tx_state *txs;
188 	int ret;
189 	int q;
190 
191 	/* Make sure our CPU doesn't change whilst we're running */
192 	sched_pin();
193 
194 	/*
195 	 * Map flowid / curcpu to a given transmit queue.
196 	 *
197 	 * Since we're running on a platform with either two
198 	 * or four CPUs, we want to distribute the load to a set
199 	 * of TX queues that won't clash with any other CPU TX queue
200 	 * use.
201 	 */
202 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
203 		/* Map flowid to a queue */
204 		q = m->m_pkthdr.flowid % sc->sc_config.num_tx_queue_per_cpu;
205 
206 		/* Now, map the queue to a set of unique queues per CPU */
207 		q = q << (mp_ncpus * curcpu);
208 
209 		/* And ensure we're not overflowing */
210 		q = q % QCOM_ESS_EDMA_NUM_TX_RINGS;
211 	} else {
212 		/*
213 		 * Use the first TXQ in each CPU group, so we don't
214 		 * hit lock contention with traffic that has flowids.
215 		 */
216 		q = (mp_ncpus * curcpu) % QCOM_ESS_EDMA_NUM_TX_RINGS;
217 	}
218 
219 	/* Attempt to enqueue in the buf_ring. */
220 	/*
221 	 * XXX TODO: maybe move this into *tx.c so gmac.c doesn't
222 	 * need to reach into the tx_state stuff?
223 	 */
224 	txs = &sc->sc_tx_state[q];
225 
226 	/* XXX TODO: add an mbuf tag instead? for the transmit gmac/ifp ? */
227 	m->m_pkthdr.rcvif = ifp;
228 
229 	ret = buf_ring_enqueue(txs->br, m);
230 
231 	if (ret == 0) {
232 		if (atomic_cmpset_int(&txs->enqueue_is_running, 0, 1) == 1) {
233 			taskqueue_enqueue(txs->completion_tq, &txs->xmit_task);
234 		}
235 	}
236 
237 	sched_unpin();
238 
239 	/* Don't consume mbuf; if_transmit caller will if needed */
240 	return (ret);
241 }
242 
243 static void
qcom_ess_edma_gmac_qflush(if_t ifp)244 qcom_ess_edma_gmac_qflush(if_t ifp)
245 {
246 	struct qcom_ess_edma_gmac *gmac = if_getsoftc(ifp);
247 	struct qcom_ess_edma_softc *sc = gmac->sc;
248 
249 	/* XXX TODO */
250 	device_printf(sc->sc_dev, "%s: gmac%d: called\n",
251 	    __func__,
252 	    gmac->id);
253 
254 	/*
255 	 * Flushing the ifnet would, sigh, require walking each buf_ring
256 	 * and then removing /only/ the entries matching that ifnet.
257 	 * Which is a complete pain to do right now.
258 	 */
259 }
260 
261 int
qcom_ess_edma_gmac_parse(struct qcom_ess_edma_softc * sc,int gmac_id)262 qcom_ess_edma_gmac_parse(struct qcom_ess_edma_softc *sc, int gmac_id)
263 {
264 	struct qcom_ess_edma_gmac *gmac;
265 	char gmac_name[10];
266 	uint32_t vlan_tag[2];
267 	phandle_t p;
268 	int len;
269 
270 	sprintf(gmac_name, "gmac%d", gmac_id);
271 
272 	gmac = &sc->sc_gmac[gmac_id];
273 
274 	/* Find our sub-device */
275 	p = ofw_bus_find_child(ofw_bus_get_node(sc->sc_dev), gmac_name);
276 	if (p <= 0) {
277 		device_printf(sc->sc_dev,
278 		    "%s: couldn't find %s\n", __func__,
279 		    gmac_name);
280 		return (ENOENT);
281 	}
282 
283 	/* local-mac-address */
284 	len = OF_getprop(p, "local-mac-address", (void *) &gmac->eaddr,
285 	    sizeof(struct ether_addr));
286 	if (len != sizeof(struct ether_addr)) {
287 		device_printf(sc->sc_dev,
288 		    "gmac%d: Couldn't parse local-mac-address\n",
289 		    gmac_id);
290 		memset(&gmac->eaddr, 0, sizeof(gmac->eaddr));
291 	}
292 
293 	/* vlan-tag - <id portmask> tuple */
294 	len = OF_getproplen(p, "vlan_tag");
295 	if (len != sizeof(vlan_tag)) {
296 		device_printf(sc->sc_dev,
297 		    "gmac%d: no vlan_tag field or invalid size/values\n",
298 		    gmac_id);
299 		return (EINVAL);
300 	}
301 	len = OF_getencprop(p, "vlan_tag", (void *) &vlan_tag,
302 	    sizeof(vlan_tag));
303 	if (len != sizeof(vlan_tag)) {
304 		device_printf(sc->sc_dev,
305 		    "gmac%d: couldn't parse vlan_tag field\n", gmac_id);
306 		return (EINVAL);
307 	}
308 
309 	/*
310 	 * Setup the given gmac entry.
311 	 */
312 	gmac->sc = sc;
313 	gmac->id = gmac_id;
314 	gmac->enabled = true;
315 	gmac->vlan_id = vlan_tag[0];
316 	gmac->port_mask = vlan_tag[1];
317 
318 	device_printf(sc->sc_dev,
319 	    "gmac%d: MAC=%6D, vlan id=%d, port_mask=0x%04x\n",
320 	    gmac_id,
321 	    &gmac->eaddr, ":",
322 	    gmac->vlan_id,
323 	    gmac->port_mask);
324 
325 	return (0);
326 }
327 
328 int
qcom_ess_edma_gmac_create_ifnet(struct qcom_ess_edma_softc * sc,int gmac_id)329 qcom_ess_edma_gmac_create_ifnet(struct qcom_ess_edma_softc *sc, int gmac_id)
330 {
331 	struct qcom_ess_edma_gmac *gmac;
332 	char gmac_name[10];
333 
334 	sprintf(gmac_name, "gmac%d", gmac_id);
335 
336 	gmac = &sc->sc_gmac[gmac_id];
337 
338 	/* Skip non-setup gmacs */
339 	if (gmac->enabled == false)
340 		return (0);
341 
342 	gmac->ifp = if_alloc(IFT_ETHER);
343 	if (gmac->ifp == NULL) {
344 		device_printf(sc->sc_dev, "gmac%d: couldn't allocate ifnet\n",
345 		    gmac_id);
346 		return (ENOSPC);
347 	}
348 
349 	if_setsoftc(gmac->ifp, gmac);
350 
351 	if_initname(gmac->ifp, "gmac", gmac_id);
352 
353 	if (ETHER_IS_ZERO(gmac->eaddr.octet)) {
354 		device_printf(sc->sc_dev, "gmac%d: generating random MAC\n",
355 		    gmac_id);
356 		ether_gen_addr(gmac->ifp, (void *) &gmac->eaddr.octet);
357 	}
358 
359 	if_setflags(gmac->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
360 
361 	if_setioctlfn(gmac->ifp, qcom_ess_edma_gmac_ioctl);
362 	if_setinitfn(gmac->ifp, qcom_ess_edma_gmac_init);
363 	if_settransmitfn(gmac->ifp, qcom_ess_edma_gmac_transmit);
364 	if_setqflushfn(gmac->ifp, qcom_ess_edma_gmac_qflush);
365 
366 	if_setcapabilitiesbit(gmac->ifp, IFCAP_VLAN_MTU |
367 	    IFCAP_VLAN_HWTAGGING, 0);
368 
369 	if_setcapabilitiesbit(gmac->ifp, IFCAP_RXCSUM, 0);
370 
371 	/* CSUM_TCP | CSUM_UDP for TX checksum offload */
372 	if_clearhwassist(gmac->ifp);
373 
374 	/* Configure a hard-coded media */
375 	ifmedia_init(&gmac->ifm, 0, qcom_ess_edma_gmac_mediachange,
376 	    qcom_ess_edma_gmac_mediastatus);
377 	ifmedia_add(&gmac->ifm, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
378 	ifmedia_set(&gmac->ifm, IFM_ETHER | IFM_1000_T | IFM_FDX);
379 
380 	ether_ifattach(gmac->ifp, (char *) &gmac->eaddr);
381 
382 	if_setcapenable(gmac->ifp, if_getcapabilities(gmac->ifp));
383 
384 	return (0);
385 }
386 
387 /*
388  * Setup the port mapping for the given GMAC.
389  *
390  * This populates sc->sc_gmac_port_map[] to point the given port
391  * entry to this gmac index.  The receive path code can then use
392  * this to figure out which gmac ifp to push a receive frame into.
393  */
394 int
qcom_ess_edma_gmac_setup_port_mapping(struct qcom_ess_edma_softc * sc,int gmac_id)395 qcom_ess_edma_gmac_setup_port_mapping(struct qcom_ess_edma_softc *sc,
396     int gmac_id)
397 {
398 	struct qcom_ess_edma_gmac *gmac;
399 	int i;
400 
401 	gmac = &sc->sc_gmac[gmac_id];
402 
403 	/* Skip non-setup gmacs */
404 	if (gmac->enabled == false)
405 		return (0);
406 
407 	for (i = 0; i < QCOM_ESS_EDMA_MAX_NUM_PORTS; i++) {
408 		if ((gmac->port_mask & (1U << i)) == 0)
409 			continue;
410 		if (sc->sc_gmac_port_map[i] != -1) {
411 			device_printf(sc->sc_dev,
412 			    "DUPLICATE GMAC port map (port %d)\n",
413 			    i);
414 			return (ENXIO);
415 		}
416 
417 		sc->sc_gmac_port_map[i] = gmac_id;
418 
419 		if (bootverbose)
420 			device_printf(sc->sc_dev,
421 			    "ESS port %d maps to gmac%d\n",
422 			    i, gmac_id);
423 	}
424 
425 	return (0);
426 }
427 
428 /*
429  * Receive frames to into the network stack.
430  *
431  * This takes a list of mbufs in the mbufq and receives them
432  * up into the appopriate ifnet context.  It takes care of
433  * the network epoch as well.
434  *
435  * This must be called with no locks held.
436  */
437 int
qcom_ess_edma_gmac_receive_frames(struct qcom_ess_edma_softc * sc,int rx_queue,struct mbufq * mq)438 qcom_ess_edma_gmac_receive_frames(struct qcom_ess_edma_softc *sc,
439     int rx_queue, struct mbufq *mq)
440 {
441 	struct qcom_ess_edma_desc_ring *ring;
442 	struct epoch_tracker et;
443 	struct mbuf *m;
444 	if_t ifp;
445 
446 	ring = &sc->sc_rx_ring[rx_queue];
447 
448 	NET_EPOCH_ENTER(et);
449 	while ((m = mbufq_dequeue(mq)) != NULL) {
450 		if (m->m_pkthdr.rcvif == NULL) {
451 			ring->stats.num_rx_no_gmac++;
452 			m_freem(m);
453 		} else {
454 			ring->stats.num_rx_ok++;
455 			ifp = m->m_pkthdr.rcvif;
456 			if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
457 			if_input(ifp, m);
458 		}
459 	}
460 	NET_EPOCH_EXIT(et);
461 	return (0);
462 }
463