1757e5b29SNathan Whitehorn /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
371e3c308SPedro F. Giffuni *
4757e5b29SNathan Whitehorn * Copyright 2013 Nathan Whitehorn
5757e5b29SNathan Whitehorn * All rights reserved.
6757e5b29SNathan Whitehorn *
7757e5b29SNathan Whitehorn * Redistribution and use in source and binary forms, with or without
8757e5b29SNathan Whitehorn * modification, are permitted provided that the following conditions
9757e5b29SNathan Whitehorn * are met:
10757e5b29SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright
11757e5b29SNathan Whitehorn * notice, this list of conditions and the following disclaimer.
12757e5b29SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright
13757e5b29SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the
14757e5b29SNathan Whitehorn * documentation and/or other materials provided with the distribution.
15757e5b29SNathan Whitehorn *
16757e5b29SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17757e5b29SNathan Whitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18757e5b29SNathan Whitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19757e5b29SNathan Whitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20757e5b29SNathan Whitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21757e5b29SNathan Whitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22757e5b29SNathan Whitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23757e5b29SNathan Whitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24757e5b29SNathan Whitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25757e5b29SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26757e5b29SNathan Whitehorn * SUCH DAMAGE.
27757e5b29SNathan Whitehorn */
28757e5b29SNathan Whitehorn
29757e5b29SNathan Whitehorn #include <sys/param.h>
30757e5b29SNathan Whitehorn #include <sys/systm.h>
31757e5b29SNathan Whitehorn #include <sys/sockio.h>
32757e5b29SNathan Whitehorn #include <sys/endian.h>
3366e01d73SGleb Smirnoff #include <sys/lock.h>
34757e5b29SNathan Whitehorn #include <sys/mbuf.h>
35757e5b29SNathan Whitehorn #include <sys/module.h>
36757e5b29SNathan Whitehorn #include <sys/malloc.h>
3766e01d73SGleb Smirnoff #include <sys/mutex.h>
38757e5b29SNathan Whitehorn #include <sys/kernel.h>
39757e5b29SNathan Whitehorn #include <sys/socket.h>
40757e5b29SNathan Whitehorn
41757e5b29SNathan Whitehorn #include <net/bpf.h>
42757e5b29SNathan Whitehorn #include <net/if.h>
4366e01d73SGleb Smirnoff #include <net/if_var.h>
44757e5b29SNathan Whitehorn #include <net/ethernet.h>
45757e5b29SNathan Whitehorn #include <net/if_dl.h>
46757e5b29SNathan Whitehorn #include <net/if_media.h>
47757e5b29SNathan Whitehorn #include <net/if_types.h>
48757e5b29SNathan Whitehorn
49757e5b29SNathan Whitehorn #include <dev/ofw/openfirm.h>
50757e5b29SNathan Whitehorn #include <dev/ofw/ofw_bus.h>
51757e5b29SNathan Whitehorn #include <dev/ofw/ofw_bus_subr.h>
52757e5b29SNathan Whitehorn #include <machine/bus.h>
53757e5b29SNathan Whitehorn #include <machine/resource.h>
54757e5b29SNathan Whitehorn #include <sys/bus.h>
55757e5b29SNathan Whitehorn #include <sys/rman.h>
56757e5b29SNathan Whitehorn
57757e5b29SNathan Whitehorn #include <powerpc/pseries/phyp-hvcall.h>
58757e5b29SNathan Whitehorn
59757e5b29SNathan Whitehorn #define LLAN_MAX_RX_PACKETS 100
60757e5b29SNathan Whitehorn #define LLAN_MAX_TX_PACKETS 100
61757e5b29SNathan Whitehorn #define LLAN_RX_BUF_LEN 8*PAGE_SIZE
62757e5b29SNathan Whitehorn
636c20c40fSNathan Whitehorn #define LLAN_BUFDESC_VALID (1ULL << 63)
646c20c40fSNathan Whitehorn #define LLAN_ADD_MULTICAST 0x1
656c20c40fSNathan Whitehorn #define LLAN_DEL_MULTICAST 0x2
666c20c40fSNathan Whitehorn #define LLAN_CLEAR_MULTICAST 0x3
676c20c40fSNathan Whitehorn
68757e5b29SNathan Whitehorn struct llan_xfer {
69757e5b29SNathan Whitehorn struct mbuf *rx_mbuf;
70757e5b29SNathan Whitehorn bus_dmamap_t rx_dmamap;
71757e5b29SNathan Whitehorn uint64_t rx_bufdesc;
72757e5b29SNathan Whitehorn };
73757e5b29SNathan Whitehorn
74757e5b29SNathan Whitehorn struct llan_receive_queue_entry { /* PAPR page 539 */
75757e5b29SNathan Whitehorn uint8_t control;
76757e5b29SNathan Whitehorn uint8_t reserved;
77757e5b29SNathan Whitehorn uint16_t offset;
78757e5b29SNathan Whitehorn uint32_t length;
79757e5b29SNathan Whitehorn uint64_t handle;
80757e5b29SNathan Whitehorn } __packed;
81757e5b29SNathan Whitehorn
82757e5b29SNathan Whitehorn struct llan_softc {
83757e5b29SNathan Whitehorn device_t dev;
84757e5b29SNathan Whitehorn struct mtx io_lock;
85757e5b29SNathan Whitehorn
86757e5b29SNathan Whitehorn cell_t unit;
87757e5b29SNathan Whitehorn uint8_t mac_address[8];
88757e5b29SNathan Whitehorn
89e68826d2SNathan Whitehorn struct ifmedia media;
90e68826d2SNathan Whitehorn
91757e5b29SNathan Whitehorn int irqid;
92757e5b29SNathan Whitehorn struct resource *irq;
93757e5b29SNathan Whitehorn void *irq_cookie;
94757e5b29SNathan Whitehorn
95757e5b29SNathan Whitehorn bus_dma_tag_t rx_dma_tag;
96757e5b29SNathan Whitehorn bus_dma_tag_t rxbuf_dma_tag;
97757e5b29SNathan Whitehorn bus_dma_tag_t tx_dma_tag;
98757e5b29SNathan Whitehorn
99757e5b29SNathan Whitehorn bus_dmamap_t tx_dma_map;
100757e5b29SNathan Whitehorn
101757e5b29SNathan Whitehorn struct llan_receive_queue_entry *rx_buf;
102757e5b29SNathan Whitehorn int rx_dma_slot;
103757e5b29SNathan Whitehorn int rx_valid_val;
104757e5b29SNathan Whitehorn bus_dmamap_t rx_buf_map;
105757e5b29SNathan Whitehorn bus_addr_t rx_buf_phys;
106757e5b29SNathan Whitehorn bus_size_t rx_buf_len;
107757e5b29SNathan Whitehorn bus_addr_t input_buf_phys;
108757e5b29SNathan Whitehorn bus_addr_t filter_buf_phys;
109757e5b29SNathan Whitehorn struct llan_xfer rx_xfer[LLAN_MAX_RX_PACKETS];
110757e5b29SNathan Whitehorn
111757e5b29SNathan Whitehorn struct ifnet *ifp;
112757e5b29SNathan Whitehorn };
113757e5b29SNathan Whitehorn
114757e5b29SNathan Whitehorn static int llan_probe(device_t);
115757e5b29SNathan Whitehorn static int llan_attach(device_t);
116757e5b29SNathan Whitehorn static void llan_intr(void *xsc);
117757e5b29SNathan Whitehorn static void llan_init(void *xsc);
118757e5b29SNathan Whitehorn static void llan_start(struct ifnet *ifp);
119757e5b29SNathan Whitehorn static int llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
120e68826d2SNathan Whitehorn static void llan_media_status(struct ifnet *ifp, struct ifmediareq *ifmr);
121e68826d2SNathan Whitehorn static int llan_media_change(struct ifnet *ifp);
122757e5b29SNathan Whitehorn static void llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs,
123757e5b29SNathan Whitehorn int err);
124757e5b29SNathan Whitehorn static int llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx);
1256c20c40fSNathan Whitehorn static int llan_set_multicast(struct llan_softc *sc);
126757e5b29SNathan Whitehorn
127757e5b29SNathan Whitehorn static device_method_t llan_methods[] = {
128757e5b29SNathan Whitehorn DEVMETHOD(device_probe, llan_probe),
129757e5b29SNathan Whitehorn DEVMETHOD(device_attach, llan_attach),
130757e5b29SNathan Whitehorn
131757e5b29SNathan Whitehorn DEVMETHOD_END
132757e5b29SNathan Whitehorn };
1339b9a5327SJohn Baldwin
134757e5b29SNathan Whitehorn static driver_t llan_driver = {
135757e5b29SNathan Whitehorn "llan",
136757e5b29SNathan Whitehorn llan_methods,
137757e5b29SNathan Whitehorn sizeof(struct llan_softc)
138757e5b29SNathan Whitehorn };
1399b9a5327SJohn Baldwin
1409b9a5327SJohn Baldwin DRIVER_MODULE(llan, vdevice, llan_driver, 0, 0);
141757e5b29SNathan Whitehorn
142757e5b29SNathan Whitehorn static int
llan_probe(device_t dev)143757e5b29SNathan Whitehorn llan_probe(device_t dev)
144757e5b29SNathan Whitehorn {
145757e5b29SNathan Whitehorn if (!ofw_bus_is_compatible(dev,"IBM,l-lan"))
146757e5b29SNathan Whitehorn return (ENXIO);
147757e5b29SNathan Whitehorn
148757e5b29SNathan Whitehorn device_set_desc(dev, "POWER Hypervisor Virtual Ethernet");
149757e5b29SNathan Whitehorn return (0);
150757e5b29SNathan Whitehorn }
151757e5b29SNathan Whitehorn
152757e5b29SNathan Whitehorn static int
llan_attach(device_t dev)153757e5b29SNathan Whitehorn llan_attach(device_t dev)
154757e5b29SNathan Whitehorn {
155757e5b29SNathan Whitehorn struct llan_softc *sc;
156757e5b29SNathan Whitehorn phandle_t node;
157b2a8b342SJohn Baldwin int i;
15857d0d4a2SLeandro Lupori ssize_t len;
159757e5b29SNathan Whitehorn
160757e5b29SNathan Whitehorn sc = device_get_softc(dev);
161757e5b29SNathan Whitehorn sc->dev = dev;
162757e5b29SNathan Whitehorn
163757e5b29SNathan Whitehorn /* Get firmware properties */
164757e5b29SNathan Whitehorn node = ofw_bus_get_node(dev);
16557d0d4a2SLeandro Lupori len = OF_getprop(node, "local-mac-address", sc->mac_address,
166757e5b29SNathan Whitehorn sizeof(sc->mac_address));
16757d0d4a2SLeandro Lupori /* If local-mac-address property has only 6 bytes (ETHER_ADDR_LEN)
16857d0d4a2SLeandro Lupori * instead of 8 (sizeof(sc->mac_address)), then its value must be
16957d0d4a2SLeandro Lupori * shifted 2 bytes to the right. */
17057d0d4a2SLeandro Lupori if (len == ETHER_ADDR_LEN) {
17157d0d4a2SLeandro Lupori bcopy(sc->mac_address, &sc->mac_address[2], len);
17257d0d4a2SLeandro Lupori /* Zero out the first 2 bytes. */
17357d0d4a2SLeandro Lupori bzero(sc->mac_address, 2);
17457d0d4a2SLeandro Lupori }
175509142e1SNathan Whitehorn OF_getencprop(node, "reg", &sc->unit, sizeof(sc->unit));
176757e5b29SNathan Whitehorn
177757e5b29SNathan Whitehorn mtx_init(&sc->io_lock, "llan", NULL, MTX_DEF);
178757e5b29SNathan Whitehorn
179757e5b29SNathan Whitehorn /* Setup interrupt */
180757e5b29SNathan Whitehorn sc->irqid = 0;
181757e5b29SNathan Whitehorn sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
182757e5b29SNathan Whitehorn RF_ACTIVE);
183757e5b29SNathan Whitehorn
184757e5b29SNathan Whitehorn if (!sc->irq) {
185757e5b29SNathan Whitehorn device_printf(dev, "Could not allocate IRQ\n");
186757e5b29SNathan Whitehorn mtx_destroy(&sc->io_lock);
187757e5b29SNathan Whitehorn return (ENXIO);
188757e5b29SNathan Whitehorn }
189757e5b29SNathan Whitehorn
190de086f1aSGleb Smirnoff bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE |
191757e5b29SNathan Whitehorn INTR_ENTROPY, NULL, llan_intr, sc, &sc->irq_cookie);
192757e5b29SNathan Whitehorn
193757e5b29SNathan Whitehorn /* Setup DMA */
194b2a8b342SJohn Baldwin bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0,
195757e5b29SNathan Whitehorn BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
196757e5b29SNathan Whitehorn LLAN_RX_BUF_LEN, 1, BUS_SPACE_MAXSIZE_32BIT,
197757e5b29SNathan Whitehorn 0, NULL, NULL, &sc->rx_dma_tag);
198b2a8b342SJohn Baldwin bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0,
199757e5b29SNathan Whitehorn BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
200757e5b29SNathan Whitehorn BUS_SPACE_MAXSIZE, 1, BUS_SPACE_MAXSIZE_32BIT,
201757e5b29SNathan Whitehorn 0, NULL, NULL, &sc->rxbuf_dma_tag);
202b2a8b342SJohn Baldwin bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
203757e5b29SNathan Whitehorn BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
204757e5b29SNathan Whitehorn BUS_SPACE_MAXSIZE, 6, BUS_SPACE_MAXSIZE_32BIT, 0,
205757e5b29SNathan Whitehorn busdma_lock_mutex, &sc->io_lock, &sc->tx_dma_tag);
206757e5b29SNathan Whitehorn
207b2a8b342SJohn Baldwin bus_dmamem_alloc(sc->rx_dma_tag, (void **)&sc->rx_buf,
208757e5b29SNathan Whitehorn BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx_buf_map);
209b2a8b342SJohn Baldwin bus_dmamap_load(sc->rx_dma_tag, sc->rx_buf_map, sc->rx_buf,
210757e5b29SNathan Whitehorn LLAN_RX_BUF_LEN, llan_rx_load_cb, sc, 0);
211757e5b29SNathan Whitehorn
212757e5b29SNathan Whitehorn /* TX DMA maps */
213757e5b29SNathan Whitehorn bus_dmamap_create(sc->tx_dma_tag, 0, &sc->tx_dma_map);
214757e5b29SNathan Whitehorn
215757e5b29SNathan Whitehorn /* RX DMA */
216757e5b29SNathan Whitehorn for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) {
217b2a8b342SJohn Baldwin bus_dmamap_create(sc->rxbuf_dma_tag, 0,
218757e5b29SNathan Whitehorn &sc->rx_xfer[i].rx_dmamap);
219757e5b29SNathan Whitehorn sc->rx_xfer[i].rx_mbuf = NULL;
220757e5b29SNathan Whitehorn }
221757e5b29SNathan Whitehorn
222757e5b29SNathan Whitehorn /* Attach to network stack */
223757e5b29SNathan Whitehorn sc->ifp = if_alloc(IFT_ETHER);
2243caaaa56SJustin Hibbits if_setsoftc(sc->ifp, sc);
225757e5b29SNathan Whitehorn
226757e5b29SNathan Whitehorn if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev));
2273caaaa56SJustin Hibbits if_setmtu(sc->ifp, ETHERMTU); /* XXX max-frame-size from OF? */
2283caaaa56SJustin Hibbits if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
2293caaaa56SJustin Hibbits if_sethwassist(sc->ifp, 0); /* XXX: ibm,illan-options */
2303caaaa56SJustin Hibbits if_setcapabilities(sc->ifp, 0);
2313caaaa56SJustin Hibbits if_setcapenable(sc->ifp, 0);
2323caaaa56SJustin Hibbits if_setstartfn(sc->ifp, llan_start);
2333caaaa56SJustin Hibbits if_setioctlfn(sc->ifp, llan_ioctl);
2343caaaa56SJustin Hibbits if_setinitfn(sc->ifp, llan_init);
235757e5b29SNathan Whitehorn
236e68826d2SNathan Whitehorn ifmedia_init(&sc->media, IFM_IMASK, llan_media_change,
237e68826d2SNathan Whitehorn llan_media_status);
238e68826d2SNathan Whitehorn ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL);
239e68826d2SNathan Whitehorn ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO);
240e68826d2SNathan Whitehorn
2413caaaa56SJustin Hibbits if_setsendqlen(sc->ifp, LLAN_MAX_RX_PACKETS);
2423caaaa56SJustin Hibbits if_setsendqready(sc->ifp);
243757e5b29SNathan Whitehorn
244757e5b29SNathan Whitehorn ether_ifattach(sc->ifp, &sc->mac_address[2]);
245757e5b29SNathan Whitehorn
246e68826d2SNathan Whitehorn /* We don't have link state reporting, so make it always up */
247e68826d2SNathan Whitehorn if_link_state_change(sc->ifp, LINK_STATE_UP);
248e68826d2SNathan Whitehorn
249757e5b29SNathan Whitehorn return (0);
250757e5b29SNathan Whitehorn }
251757e5b29SNathan Whitehorn
252e68826d2SNathan Whitehorn static int
llan_media_change(struct ifnet * ifp)253e68826d2SNathan Whitehorn llan_media_change(struct ifnet *ifp)
254e68826d2SNathan Whitehorn {
2553caaaa56SJustin Hibbits struct llan_softc *sc = if_getsoftc(ifp);
256e68826d2SNathan Whitehorn
257e68826d2SNathan Whitehorn if (IFM_TYPE(sc->media.ifm_media) != IFM_ETHER)
258e68826d2SNathan Whitehorn return (EINVAL);
259e68826d2SNathan Whitehorn
260e68826d2SNathan Whitehorn if (IFM_SUBTYPE(sc->media.ifm_media) != IFM_AUTO)
261e68826d2SNathan Whitehorn return (EINVAL);
262e68826d2SNathan Whitehorn
263e68826d2SNathan Whitehorn return (0);
264e68826d2SNathan Whitehorn }
265e68826d2SNathan Whitehorn
266e68826d2SNathan Whitehorn static void
llan_media_status(struct ifnet * ifp,struct ifmediareq * ifmr)267e68826d2SNathan Whitehorn llan_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
268e68826d2SNathan Whitehorn {
269e68826d2SNathan Whitehorn
270e68826d2SNathan Whitehorn ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE | IFM_UNKNOWN | IFM_FDX;
271e68826d2SNathan Whitehorn ifmr->ifm_active = IFM_ETHER;
272e68826d2SNathan Whitehorn }
273e68826d2SNathan Whitehorn
274757e5b29SNathan Whitehorn static void
llan_rx_load_cb(void * xsc,bus_dma_segment_t * segs,int nsegs,int err)275757e5b29SNathan Whitehorn llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err)
276757e5b29SNathan Whitehorn {
277757e5b29SNathan Whitehorn struct llan_softc *sc = xsc;
278757e5b29SNathan Whitehorn
279757e5b29SNathan Whitehorn sc->rx_buf_phys = segs[0].ds_addr;
280757e5b29SNathan Whitehorn sc->rx_buf_len = segs[0].ds_len - 2*PAGE_SIZE;
281757e5b29SNathan Whitehorn sc->input_buf_phys = segs[0].ds_addr + segs[0].ds_len - PAGE_SIZE;
282757e5b29SNathan Whitehorn sc->filter_buf_phys = segs[0].ds_addr + segs[0].ds_len - 2*PAGE_SIZE;
283757e5b29SNathan Whitehorn }
284757e5b29SNathan Whitehorn
285757e5b29SNathan Whitehorn static void
llan_init(void * xsc)286757e5b29SNathan Whitehorn llan_init(void *xsc)
287757e5b29SNathan Whitehorn {
288757e5b29SNathan Whitehorn struct llan_softc *sc = xsc;
289757e5b29SNathan Whitehorn uint64_t rx_buf_desc;
290757e5b29SNathan Whitehorn uint64_t macaddr;
291b2a8b342SJohn Baldwin int i;
292757e5b29SNathan Whitehorn
293757e5b29SNathan Whitehorn mtx_lock(&sc->io_lock);
294757e5b29SNathan Whitehorn
295757e5b29SNathan Whitehorn phyp_hcall(H_FREE_LOGICAL_LAN, sc->unit);
296757e5b29SNathan Whitehorn
297757e5b29SNathan Whitehorn /* Create buffers (page 539) */
298757e5b29SNathan Whitehorn sc->rx_dma_slot = 0;
299757e5b29SNathan Whitehorn sc->rx_valid_val = 1;
300757e5b29SNathan Whitehorn
3016c20c40fSNathan Whitehorn rx_buf_desc = LLAN_BUFDESC_VALID;
302757e5b29SNathan Whitehorn rx_buf_desc |= (sc->rx_buf_len << 32);
303757e5b29SNathan Whitehorn rx_buf_desc |= sc->rx_buf_phys;
304757e5b29SNathan Whitehorn memcpy(&macaddr, sc->mac_address, 8);
305b2a8b342SJohn Baldwin phyp_hcall(H_REGISTER_LOGICAL_LAN, sc->unit, sc->input_buf_phys,
306757e5b29SNathan Whitehorn rx_buf_desc, sc->filter_buf_phys, macaddr);
307757e5b29SNathan Whitehorn
308757e5b29SNathan Whitehorn for (i = 0; i < LLAN_MAX_RX_PACKETS; i++)
309757e5b29SNathan Whitehorn llan_add_rxbuf(sc, &sc->rx_xfer[i]);
310757e5b29SNathan Whitehorn
311757e5b29SNathan Whitehorn phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */
312757e5b29SNathan Whitehorn
313757e5b29SNathan Whitehorn /* Tell stack we're up */
3143caaaa56SJustin Hibbits if_setdrvflagbits(sc->ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
315757e5b29SNathan Whitehorn
316757e5b29SNathan Whitehorn mtx_unlock(&sc->io_lock);
3178704b9e9SNathan Whitehorn
3188704b9e9SNathan Whitehorn /* Check for pending receives scheduled before interrupt enable */
3198704b9e9SNathan Whitehorn llan_intr(sc);
320757e5b29SNathan Whitehorn }
321757e5b29SNathan Whitehorn
322757e5b29SNathan Whitehorn static int
llan_add_rxbuf(struct llan_softc * sc,struct llan_xfer * rx)323757e5b29SNathan Whitehorn llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx)
324757e5b29SNathan Whitehorn {
325757e5b29SNathan Whitehorn struct mbuf *m;
326757e5b29SNathan Whitehorn bus_dma_segment_t segs[1];
327757e5b29SNathan Whitehorn int error, nsegs;
328757e5b29SNathan Whitehorn
329757e5b29SNathan Whitehorn mtx_assert(&sc->io_lock, MA_OWNED);
330757e5b29SNathan Whitehorn
331757e5b29SNathan Whitehorn m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
332757e5b29SNathan Whitehorn if (m == NULL)
333757e5b29SNathan Whitehorn return (ENOBUFS);
334757e5b29SNathan Whitehorn
335757e5b29SNathan Whitehorn m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
336757e5b29SNathan Whitehorn if (rx->rx_mbuf != NULL) {
337757e5b29SNathan Whitehorn bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap,
338757e5b29SNathan Whitehorn BUS_DMASYNC_POSTREAD);
339757e5b29SNathan Whitehorn bus_dmamap_unload(sc->rxbuf_dma_tag, rx->rx_dmamap);
340757e5b29SNathan Whitehorn }
341757e5b29SNathan Whitehorn
342757e5b29SNathan Whitehorn /* Save pointer to buffer structure */
343757e5b29SNathan Whitehorn m_copyback(m, 0, 8, (void *)&rx);
344757e5b29SNathan Whitehorn
345757e5b29SNathan Whitehorn error = bus_dmamap_load_mbuf_sg(sc->rxbuf_dma_tag, rx->rx_dmamap, m,
346757e5b29SNathan Whitehorn segs, &nsegs, BUS_DMA_NOWAIT);
347757e5b29SNathan Whitehorn if (error != 0) {
348757e5b29SNathan Whitehorn device_printf(sc->dev,
349757e5b29SNathan Whitehorn "cannot load RX DMA map %p, error = %d\n", rx, error);
350757e5b29SNathan Whitehorn m_freem(m);
351757e5b29SNathan Whitehorn return (error);
352757e5b29SNathan Whitehorn }
353757e5b29SNathan Whitehorn
354757e5b29SNathan Whitehorn /* If nsegs is wrong then the stack is corrupt. */
355757e5b29SNathan Whitehorn KASSERT(nsegs == 1,
356757e5b29SNathan Whitehorn ("%s: too many DMA segments (%d)", __func__, nsegs));
357757e5b29SNathan Whitehorn rx->rx_mbuf = m;
358757e5b29SNathan Whitehorn
359757e5b29SNathan Whitehorn bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, BUS_DMASYNC_PREREAD);
360757e5b29SNathan Whitehorn
3616c20c40fSNathan Whitehorn rx->rx_bufdesc = LLAN_BUFDESC_VALID;
362757e5b29SNathan Whitehorn rx->rx_bufdesc |= (((uint64_t)segs[0].ds_len) << 32);
363757e5b29SNathan Whitehorn rx->rx_bufdesc |= segs[0].ds_addr;
364757e5b29SNathan Whitehorn error = phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, rx->rx_bufdesc);
365757e5b29SNathan Whitehorn if (error != 0) {
366757e5b29SNathan Whitehorn m_freem(m);
367757e5b29SNathan Whitehorn rx->rx_mbuf = NULL;
368757e5b29SNathan Whitehorn return (ENOBUFS);
369757e5b29SNathan Whitehorn }
370757e5b29SNathan Whitehorn
371757e5b29SNathan Whitehorn return (0);
372757e5b29SNathan Whitehorn }
373757e5b29SNathan Whitehorn
374757e5b29SNathan Whitehorn static void
llan_intr(void * xsc)375757e5b29SNathan Whitehorn llan_intr(void *xsc)
376757e5b29SNathan Whitehorn {
377757e5b29SNathan Whitehorn struct llan_softc *sc = xsc;
378757e5b29SNathan Whitehorn struct llan_xfer *rx;
379757e5b29SNathan Whitehorn struct mbuf *m;
380757e5b29SNathan Whitehorn
381757e5b29SNathan Whitehorn mtx_lock(&sc->io_lock);
3828704b9e9SNathan Whitehorn restart:
383757e5b29SNathan Whitehorn phyp_hcall(H_VIO_SIGNAL, sc->unit, 0);
384757e5b29SNathan Whitehorn
385757e5b29SNathan Whitehorn while ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val) {
386757e5b29SNathan Whitehorn rx = (struct llan_xfer *)sc->rx_buf[sc->rx_dma_slot].handle;
387757e5b29SNathan Whitehorn m = rx->rx_mbuf;
388757e5b29SNathan Whitehorn m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset - 8);
389757e5b29SNathan Whitehorn m->m_len = sc->rx_buf[sc->rx_dma_slot].length;
390757e5b29SNathan Whitehorn
391757e5b29SNathan Whitehorn /* llan_add_rxbuf does DMA sync and unload as well as requeue */
392757e5b29SNathan Whitehorn if (llan_add_rxbuf(sc, rx) != 0) {
393e2efa70eSGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
394757e5b29SNathan Whitehorn continue;
395757e5b29SNathan Whitehorn }
396757e5b29SNathan Whitehorn
397e2efa70eSGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1);
398757e5b29SNathan Whitehorn m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset);
399757e5b29SNathan Whitehorn m->m_len = sc->rx_buf[sc->rx_dma_slot].length;
400757e5b29SNathan Whitehorn m->m_pkthdr.rcvif = sc->ifp;
401757e5b29SNathan Whitehorn m->m_pkthdr.len = m->m_len;
402757e5b29SNathan Whitehorn sc->rx_dma_slot++;
403757e5b29SNathan Whitehorn
404757e5b29SNathan Whitehorn if (sc->rx_dma_slot >= sc->rx_buf_len/sizeof(sc->rx_buf[0])) {
405757e5b29SNathan Whitehorn sc->rx_dma_slot = 0;
406757e5b29SNathan Whitehorn sc->rx_valid_val = !sc->rx_valid_val;
407757e5b29SNathan Whitehorn }
408757e5b29SNathan Whitehorn
409757e5b29SNathan Whitehorn mtx_unlock(&sc->io_lock);
4103caaaa56SJustin Hibbits if_input(sc->ifp, m);
411757e5b29SNathan Whitehorn mtx_lock(&sc->io_lock);
412757e5b29SNathan Whitehorn }
413757e5b29SNathan Whitehorn
414757e5b29SNathan Whitehorn phyp_hcall(H_VIO_SIGNAL, sc->unit, 1);
4158704b9e9SNathan Whitehorn
4168704b9e9SNathan Whitehorn /*
4178704b9e9SNathan Whitehorn * H_VIO_SIGNAL enables interrupts for future packets only.
4188704b9e9SNathan Whitehorn * Make sure none were queued between the end of the loop and the
4198704b9e9SNathan Whitehorn * enable interrupts call.
4208704b9e9SNathan Whitehorn */
4218704b9e9SNathan Whitehorn if ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val)
4228704b9e9SNathan Whitehorn goto restart;
4238704b9e9SNathan Whitehorn
424757e5b29SNathan Whitehorn mtx_unlock(&sc->io_lock);
425757e5b29SNathan Whitehorn }
426757e5b29SNathan Whitehorn
427757e5b29SNathan Whitehorn static void
llan_send_packet(void * xsc,bus_dma_segment_t * segs,int nsegs,bus_size_t mapsize,int error)428757e5b29SNathan Whitehorn llan_send_packet(void *xsc, bus_dma_segment_t *segs, int nsegs,
429757e5b29SNathan Whitehorn bus_size_t mapsize, int error)
430757e5b29SNathan Whitehorn {
431757e5b29SNathan Whitehorn struct llan_softc *sc = xsc;
432757e5b29SNathan Whitehorn uint64_t bufdescs[6];
433f4c5f64dSJustin Hibbits int i, err;
434757e5b29SNathan Whitehorn
435757e5b29SNathan Whitehorn bzero(bufdescs, sizeof(bufdescs));
436757e5b29SNathan Whitehorn
437757e5b29SNathan Whitehorn for (i = 0; i < nsegs; i++) {
4386c20c40fSNathan Whitehorn bufdescs[i] = LLAN_BUFDESC_VALID;
439757e5b29SNathan Whitehorn bufdescs[i] |= (((uint64_t)segs[i].ds_len) << 32);
440757e5b29SNathan Whitehorn bufdescs[i] |= segs[i].ds_addr;
441757e5b29SNathan Whitehorn }
442757e5b29SNathan Whitehorn
443f4c5f64dSJustin Hibbits err = phyp_hcall(H_SEND_LOGICAL_LAN, sc->unit, bufdescs[0],
444757e5b29SNathan Whitehorn bufdescs[1], bufdescs[2], bufdescs[3], bufdescs[4], bufdescs[5], 0);
445af4c3211SNathan Whitehorn /*
446af4c3211SNathan Whitehorn * The hypercall returning implies completion -- or that the call will
447af4c3211SNathan Whitehorn * not complete. In principle, we should try a few times if we get back
448af4c3211SNathan Whitehorn * H_BUSY based on the continuation token in R4. For now, just drop
449af4c3211SNathan Whitehorn * the packet in such cases.
450af4c3211SNathan Whitehorn */
451f4c5f64dSJustin Hibbits if (err == H_SUCCESS)
452f4c5f64dSJustin Hibbits if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1);
453f4c5f64dSJustin Hibbits else
454f4c5f64dSJustin Hibbits if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
455757e5b29SNathan Whitehorn }
456757e5b29SNathan Whitehorn
457757e5b29SNathan Whitehorn static void
llan_start_locked(struct ifnet * ifp)458757e5b29SNathan Whitehorn llan_start_locked(struct ifnet *ifp)
459757e5b29SNathan Whitehorn {
4603caaaa56SJustin Hibbits struct llan_softc *sc = if_getsoftc(ifp);
461757e5b29SNathan Whitehorn int nsegs;
462757e5b29SNathan Whitehorn struct mbuf *mb_head, *m;
463757e5b29SNathan Whitehorn
464757e5b29SNathan Whitehorn mtx_assert(&sc->io_lock, MA_OWNED);
465757e5b29SNathan Whitehorn
4663caaaa56SJustin Hibbits if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
467757e5b29SNathan Whitehorn IFF_DRV_RUNNING)
468757e5b29SNathan Whitehorn return;
469757e5b29SNathan Whitehorn
4703caaaa56SJustin Hibbits while (!if_sendq_empty(ifp)) {
4713caaaa56SJustin Hibbits mb_head = if_dequeue(ifp);
472757e5b29SNathan Whitehorn
473757e5b29SNathan Whitehorn if (mb_head == NULL)
474757e5b29SNathan Whitehorn break;
475757e5b29SNathan Whitehorn
476757e5b29SNathan Whitehorn BPF_MTAP(ifp, mb_head);
477757e5b29SNathan Whitehorn
478757e5b29SNathan Whitehorn for (m = mb_head, nsegs = 0; m != NULL; m = m->m_next)
479757e5b29SNathan Whitehorn nsegs++;
480757e5b29SNathan Whitehorn if (nsegs > 6) {
481757e5b29SNathan Whitehorn m = m_collapse(mb_head, M_NOWAIT, 6);
482757e5b29SNathan Whitehorn if (m == NULL) {
483757e5b29SNathan Whitehorn m_freem(mb_head);
484757e5b29SNathan Whitehorn continue;
485757e5b29SNathan Whitehorn }
486757e5b29SNathan Whitehorn }
487757e5b29SNathan Whitehorn
488af4c3211SNathan Whitehorn bus_dmamap_load_mbuf(sc->tx_dma_tag, sc->tx_dma_map,
489757e5b29SNathan Whitehorn mb_head, llan_send_packet, sc, 0);
490af4c3211SNathan Whitehorn bus_dmamap_unload(sc->tx_dma_tag, sc->tx_dma_map);
491757e5b29SNathan Whitehorn m_freem(mb_head);
492757e5b29SNathan Whitehorn }
493757e5b29SNathan Whitehorn }
494757e5b29SNathan Whitehorn
495757e5b29SNathan Whitehorn static void
llan_start(struct ifnet * ifp)496757e5b29SNathan Whitehorn llan_start(struct ifnet *ifp)
497757e5b29SNathan Whitehorn {
4983caaaa56SJustin Hibbits struct llan_softc *sc = if_getsoftc(ifp);
499757e5b29SNathan Whitehorn
500757e5b29SNathan Whitehorn mtx_lock(&sc->io_lock);
501757e5b29SNathan Whitehorn llan_start_locked(ifp);
502757e5b29SNathan Whitehorn mtx_unlock(&sc->io_lock);
503757e5b29SNathan Whitehorn }
504757e5b29SNathan Whitehorn
50538e1a658SGleb Smirnoff static u_int
llan_set_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)50638e1a658SGleb Smirnoff llan_set_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
50738e1a658SGleb Smirnoff {
50838e1a658SGleb Smirnoff struct llan_softc *sc = arg;
50938e1a658SGleb Smirnoff uint64_t macaddr = 0;
51038e1a658SGleb Smirnoff
51138e1a658SGleb Smirnoff memcpy((uint8_t *)&macaddr + 2, LLADDR(sdl), 6);
51238e1a658SGleb Smirnoff phyp_hcall(H_MULTICAST_CTRL, sc->unit, LLAN_ADD_MULTICAST, macaddr);
51338e1a658SGleb Smirnoff
51438e1a658SGleb Smirnoff return (1);
51538e1a658SGleb Smirnoff }
51638e1a658SGleb Smirnoff
517757e5b29SNathan Whitehorn static int
llan_set_multicast(struct llan_softc * sc)5186c20c40fSNathan Whitehorn llan_set_multicast(struct llan_softc *sc)
5196c20c40fSNathan Whitehorn {
5206c20c40fSNathan Whitehorn struct ifnet *ifp = sc->ifp;
5216c20c40fSNathan Whitehorn
5226c20c40fSNathan Whitehorn mtx_assert(&sc->io_lock, MA_OWNED);
5236c20c40fSNathan Whitehorn
5246c20c40fSNathan Whitehorn phyp_hcall(H_MULTICAST_CTRL, sc->unit, LLAN_CLEAR_MULTICAST, 0);
5256c20c40fSNathan Whitehorn
52638e1a658SGleb Smirnoff if_foreach_llmaddr(ifp, llan_set_maddr, sc);
5276c20c40fSNathan Whitehorn
5286c20c40fSNathan Whitehorn return (0);
5296c20c40fSNathan Whitehorn }
5306c20c40fSNathan Whitehorn
5316c20c40fSNathan Whitehorn static int
llan_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)532757e5b29SNathan Whitehorn llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
533757e5b29SNathan Whitehorn {
5346c20c40fSNathan Whitehorn int err = 0;
5353caaaa56SJustin Hibbits struct llan_softc *sc = if_getsoftc(ifp);
536757e5b29SNathan Whitehorn
5376c20c40fSNathan Whitehorn switch (cmd) {
5386c20c40fSNathan Whitehorn case SIOCADDMULTI:
5396c20c40fSNathan Whitehorn case SIOCDELMULTI:
5406c20c40fSNathan Whitehorn mtx_lock(&sc->io_lock);
5413caaaa56SJustin Hibbits if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) != 0)
5426c20c40fSNathan Whitehorn llan_set_multicast(sc);
5436c20c40fSNathan Whitehorn mtx_unlock(&sc->io_lock);
5446c20c40fSNathan Whitehorn break;
545e68826d2SNathan Whitehorn case SIOCGIFMEDIA:
546e68826d2SNathan Whitehorn case SIOCSIFMEDIA:
547e68826d2SNathan Whitehorn err = ifmedia_ioctl(ifp, (struct ifreq *)data, &sc->media, cmd);
548e68826d2SNathan Whitehorn break;
5496c20c40fSNathan Whitehorn case SIOCSIFFLAGS:
5506c20c40fSNathan Whitehorn default:
551757e5b29SNathan Whitehorn err = ether_ioctl(ifp, cmd, data);
5526c20c40fSNathan Whitehorn break;
5536c20c40fSNathan Whitehorn }
554757e5b29SNathan Whitehorn
555757e5b29SNathan Whitehorn return (err);
556757e5b29SNathan Whitehorn }
557