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