1217214Sjmallett/*-
2217214Sjmallett * Copyright (c) 2010-2011 Juli Mallett <jmallett@FreeBSD.org>
3217214Sjmallett * All rights reserved.
4217214Sjmallett *
5217214Sjmallett * Redistribution and use in source and binary forms, with or without
6217214Sjmallett * modification, are permitted provided that the following conditions
7217214Sjmallett * are met:
8217214Sjmallett * 1. Redistributions of source code must retain the above copyright
9217214Sjmallett *    notice, this list of conditions and the following disclaimer.
10217214Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
11217214Sjmallett *    notice, this list of conditions and the following disclaimer in the
12217214Sjmallett *    documentation and/or other materials provided with the distribution.
13217214Sjmallett *
14217214Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15217214Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16217214Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17217214Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18217214Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19217214Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20217214Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21217214Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22217214Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23217214Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24217214Sjmallett * SUCH DAMAGE.
25217214Sjmallett *
26217214Sjmallett * $FreeBSD$
27217214Sjmallett */
28217214Sjmallett
29217214Sjmallett/*
30217214Sjmallett * Cavium Octeon management port Ethernet devices.
31217214Sjmallett */
32217214Sjmallett
33217214Sjmallett#include "opt_inet.h"
34217214Sjmallett
35217214Sjmallett#include <sys/param.h>
36217214Sjmallett#include <sys/systm.h>
37217214Sjmallett#include <sys/bus.h>
38217214Sjmallett#include <sys/endian.h>
39217214Sjmallett#include <sys/kernel.h>
40217214Sjmallett#include <sys/mbuf.h>
41217214Sjmallett#include <sys/lock.h>
42217214Sjmallett#include <sys/module.h>
43217214Sjmallett#include <sys/mutex.h>
44217214Sjmallett#include <sys/rman.h>
45217214Sjmallett#include <sys/socket.h>
46217214Sjmallett#include <sys/sockio.h>
47217214Sjmallett#include <sys/sysctl.h>
48217214Sjmallett
49217214Sjmallett#include <net/bpf.h>
50217214Sjmallett#include <net/ethernet.h>
51217214Sjmallett#include <net/if.h>
52217214Sjmallett#include <net/if_dl.h>
53217214Sjmallett#include <net/if_media.h>
54217214Sjmallett#include <net/if_types.h>
55217214Sjmallett#include <net/if_var.h>
56217214Sjmallett#include <net/if_vlan_var.h>
57217214Sjmallett
58217214Sjmallett#ifdef INET
59217214Sjmallett#include <netinet/in.h>
60217214Sjmallett#include <netinet/if_ether.h>
61217214Sjmallett#endif
62217214Sjmallett
63217214Sjmallett#include <contrib/octeon-sdk/cvmx.h>
64217214Sjmallett#include <contrib/octeon-sdk/cvmx-interrupt.h>
65217214Sjmallett#include <contrib/octeon-sdk/cvmx-mgmt-port.h>
66217214Sjmallett
67218594Sjmallettextern cvmx_bootinfo_t *octeon_bootinfo;
68218594Sjmallett
69217214Sjmallettstruct octm_softc {
70217214Sjmallett	struct ifnet *sc_ifp;
71217214Sjmallett	device_t sc_dev;
72217214Sjmallett	unsigned sc_port;
73217214Sjmallett	int sc_flags;
74217214Sjmallett	struct ifmedia sc_ifmedia;
75217214Sjmallett	struct resource *sc_intr;
76217214Sjmallett	void *sc_intr_cookie;
77217214Sjmallett};
78217214Sjmallett
79217214Sjmallettstatic void	octm_identify(driver_t *, device_t);
80217214Sjmallettstatic int	octm_probe(device_t);
81217214Sjmallettstatic int	octm_attach(device_t);
82217214Sjmallettstatic int	octm_detach(device_t);
83217214Sjmallettstatic int	octm_shutdown(device_t);
84217214Sjmallett
85217214Sjmallettstatic void	octm_init(void *);
86217214Sjmallettstatic int	octm_transmit(struct ifnet *, struct mbuf *);
87217214Sjmallett
88217214Sjmallettstatic int	octm_medchange(struct ifnet *);
89217214Sjmallettstatic void	octm_medstat(struct ifnet *, struct ifmediareq *);
90217214Sjmallett
91217214Sjmallettstatic int	octm_ioctl(struct ifnet *, u_long, caddr_t);
92217214Sjmallett
93217214Sjmallettstatic void	octm_rx_intr(void *);
94217214Sjmallett
95217214Sjmallettstatic device_method_t octm_methods[] = {
96217214Sjmallett	/* Device interface */
97217214Sjmallett	DEVMETHOD(device_identify,	octm_identify),
98217214Sjmallett	DEVMETHOD(device_probe,		octm_probe),
99217214Sjmallett	DEVMETHOD(device_attach,	octm_attach),
100217214Sjmallett	DEVMETHOD(device_detach,	octm_detach),
101217214Sjmallett	DEVMETHOD(device_shutdown,	octm_shutdown),
102217214Sjmallett
103217214Sjmallett	{ 0, 0 }
104217214Sjmallett};
105217214Sjmallett
106217214Sjmallettstatic driver_t octm_driver = {
107217214Sjmallett	"octm",
108217214Sjmallett	octm_methods,
109217214Sjmallett	sizeof (struct octm_softc),
110217214Sjmallett};
111217214Sjmallett
112217214Sjmallettstatic devclass_t octm_devclass;
113217214Sjmallett
114217214SjmallettDRIVER_MODULE(octm, ciu, octm_driver, octm_devclass, 0, 0);
115217214Sjmallett
116217214Sjmallettstatic void
117217214Sjmallettoctm_identify(driver_t *drv, device_t parent)
118217214Sjmallett{
119217214Sjmallett	unsigned i;
120217214Sjmallett
121217214Sjmallett	if (!octeon_has_feature(OCTEON_FEATURE_MGMT_PORT))
122217214Sjmallett		return;
123217214Sjmallett
124217214Sjmallett	for (i = 0; i < CVMX_MGMT_PORT_NUM_PORTS; i++)
125217214Sjmallett		BUS_ADD_CHILD(parent, 0, "octm", i);
126217214Sjmallett}
127217214Sjmallett
128217214Sjmallettstatic int
129217214Sjmallettoctm_probe(device_t dev)
130217214Sjmallett{
131217214Sjmallett	cvmx_mgmt_port_result_t result;
132217214Sjmallett
133217214Sjmallett	result = cvmx_mgmt_port_initialize(device_get_unit(dev));
134217214Sjmallett	switch (result) {
135217214Sjmallett	case CVMX_MGMT_PORT_SUCCESS:
136217214Sjmallett		break;
137217214Sjmallett	case CVMX_MGMT_PORT_NO_MEMORY:
138217214Sjmallett		return (ENOBUFS);
139217214Sjmallett	case CVMX_MGMT_PORT_INVALID_PARAM:
140217214Sjmallett		return (ENXIO);
141217214Sjmallett	case CVMX_MGMT_PORT_INIT_ERROR:
142217214Sjmallett		return (EIO);
143217214Sjmallett	}
144217214Sjmallett
145217214Sjmallett	device_set_desc(dev, "Cavium Octeon Management Ethernet");
146217214Sjmallett
147217214Sjmallett	return (0);
148217214Sjmallett}
149217214Sjmallett
150217214Sjmallettstatic int
151217214Sjmallettoctm_attach(device_t dev)
152217214Sjmallett{
153217214Sjmallett	struct ifnet *ifp;
154217214Sjmallett	struct octm_softc *sc;
155217214Sjmallett	cvmx_mixx_irhwm_t mixx_irhwm;
156217214Sjmallett	cvmx_mixx_intena_t mixx_intena;
157217214Sjmallett	uint64_t mac;
158217214Sjmallett	int error;
159217214Sjmallett	int irq;
160217214Sjmallett	int rid;
161217214Sjmallett
162217214Sjmallett	sc = device_get_softc(dev);
163217214Sjmallett	sc->sc_dev = dev;
164217214Sjmallett	sc->sc_port = device_get_unit(dev);
165217214Sjmallett
166217214Sjmallett	switch (sc->sc_port) {
167217214Sjmallett	case 0:
168217214Sjmallett		irq = CVMX_IRQ_MII;
169217214Sjmallett		break;
170217214Sjmallett	case 1:
171217214Sjmallett		irq = CVMX_IRQ_MII1;
172217214Sjmallett		break;
173217214Sjmallett	default:
174217214Sjmallett		device_printf(dev, "unsupported management port %u.\n", sc->sc_port);
175217214Sjmallett		return (ENXIO);
176217214Sjmallett	}
177217214Sjmallett
178219706Sjmallett	/*
179219706Sjmallett	 * Set MAC address for this management port.
180219706Sjmallett	 */
181219706Sjmallett	mac = 0;
182219706Sjmallett	memcpy((u_int8_t *)&mac + 2, octeon_bootinfo->mac_addr_base, 6);
183219706Sjmallett	mac += sc->sc_port;
184219706Sjmallett	cvmx_mgmt_port_set_mac(sc->sc_port, mac);
185217214Sjmallett
186217214Sjmallett	/* No watermark for input ring.  */
187217214Sjmallett	mixx_irhwm.u64 = 0;
188217214Sjmallett	cvmx_write_csr(CVMX_MIXX_IRHWM(sc->sc_port), mixx_irhwm.u64);
189217214Sjmallett
190217214Sjmallett	/* Enable input ring interrupts.  */
191217214Sjmallett	mixx_intena.u64 = 0;
192217214Sjmallett	mixx_intena.s.ithena = 1;
193217214Sjmallett	cvmx_write_csr(CVMX_MIXX_INTENA(sc->sc_port), mixx_intena.u64);
194217214Sjmallett
195217214Sjmallett	/* Allocate and establish interrupt.  */
196217214Sjmallett	rid = 0;
197217214Sjmallett	sc->sc_intr = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid,
198217214Sjmallett	    irq, irq, 1, RF_ACTIVE);
199217214Sjmallett	if (sc->sc_intr == NULL) {
200217214Sjmallett		device_printf(dev, "unable to allocate IRQ.\n");
201217214Sjmallett		return (ENXIO);
202217214Sjmallett	}
203217214Sjmallett
204217214Sjmallett	error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL,
205217214Sjmallett	    octm_rx_intr, sc, &sc->sc_intr_cookie);
206217214Sjmallett	if (error != 0) {
207217214Sjmallett		device_printf(dev, "unable to setup interrupt.\n");
208217214Sjmallett		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
209217214Sjmallett		return (ENXIO);
210217214Sjmallett	}
211217214Sjmallett
212217214Sjmallett	bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx");
213217214Sjmallett
214217214Sjmallett	/* XXX Possibly should enable TX interrupts.  */
215217214Sjmallett
216217214Sjmallett	ifp = if_alloc(IFT_ETHER);
217217214Sjmallett	if (ifp == NULL) {
218217214Sjmallett		device_printf(dev, "cannot allocate ifnet.\n");
219217214Sjmallett		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
220217214Sjmallett		return (ENOMEM);
221217214Sjmallett	}
222217214Sjmallett
223217214Sjmallett	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
224217214Sjmallett	ifp->if_mtu = ETHERMTU;
225217214Sjmallett	ifp->if_init = octm_init;
226217214Sjmallett	ifp->if_softc = sc;
227217214Sjmallett	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
228217214Sjmallett	ifp->if_ioctl = octm_ioctl;
229217214Sjmallett
230217214Sjmallett	sc->sc_ifp = ifp;
231217214Sjmallett	sc->sc_flags = ifp->if_flags;
232217214Sjmallett
233217214Sjmallett	ifmedia_init(&sc->sc_ifmedia, 0, octm_medchange, octm_medstat);
234217214Sjmallett
235217214Sjmallett	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
236217214Sjmallett	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
237217214Sjmallett
238217214Sjmallett	ether_ifattach(ifp, (const u_int8_t *)&mac + 2);
239217214Sjmallett
240217214Sjmallett	ifp->if_transmit = octm_transmit;
241217214Sjmallett
242217214Sjmallett	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
243217214Sjmallett	ifp->if_capabilities = IFCAP_VLAN_MTU;
244217214Sjmallett	ifp->if_capenable = ifp->if_capabilities;
245217214Sjmallett
246217214Sjmallett	IFQ_SET_MAXLEN(&ifp->if_snd, CVMX_MGMT_PORT_NUM_TX_BUFFERS);
247217214Sjmallett	ifp->if_snd.ifq_drv_maxlen = CVMX_MGMT_PORT_NUM_TX_BUFFERS;
248217214Sjmallett	IFQ_SET_READY(&ifp->if_snd);
249217214Sjmallett
250217214Sjmallett	return (bus_generic_attach(dev));
251217214Sjmallett}
252217214Sjmallett
253217214Sjmallettstatic int
254217214Sjmallettoctm_detach(device_t dev)
255217214Sjmallett{
256217214Sjmallett	struct octm_softc *sc;
257217214Sjmallett	cvmx_mgmt_port_result_t result;
258217214Sjmallett
259217214Sjmallett	sc = device_get_softc(dev);
260217214Sjmallett
261217214Sjmallett	result = cvmx_mgmt_port_initialize(sc->sc_port);
262217214Sjmallett	switch (result) {
263217214Sjmallett	case CVMX_MGMT_PORT_SUCCESS:
264217214Sjmallett		break;
265217214Sjmallett	case CVMX_MGMT_PORT_NO_MEMORY:
266217214Sjmallett		return (ENOBUFS);
267217214Sjmallett	case CVMX_MGMT_PORT_INVALID_PARAM:
268217214Sjmallett		return (ENXIO);
269217214Sjmallett	case CVMX_MGMT_PORT_INIT_ERROR:
270217214Sjmallett		return (EIO);
271217214Sjmallett	}
272217214Sjmallett
273217214Sjmallett	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
274217214Sjmallett	/* XXX Incomplete.  */
275217214Sjmallett
276217214Sjmallett	return (0);
277217214Sjmallett}
278217214Sjmallett
279217214Sjmallettstatic int
280217214Sjmallettoctm_shutdown(device_t dev)
281217214Sjmallett{
282217214Sjmallett	return (octm_detach(dev));
283217214Sjmallett}
284217214Sjmallett
285217214Sjmallettstatic void
286217214Sjmallettoctm_init(void *arg)
287217214Sjmallett{
288217214Sjmallett	struct ifnet *ifp;
289217214Sjmallett	struct octm_softc *sc;
290217214Sjmallett	cvmx_mgmt_port_netdevice_flags_t flags;
291217214Sjmallett	uint64_t mac;
292217214Sjmallett
293217214Sjmallett	sc = arg;
294217214Sjmallett	ifp = sc->sc_ifp;
295217214Sjmallett
296217214Sjmallett	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
297217214Sjmallett		cvmx_mgmt_port_disable(sc->sc_port);
298217214Sjmallett
299217214Sjmallett		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
300217214Sjmallett	}
301217214Sjmallett
302217214Sjmallett	if (((ifp->if_flags ^ sc->sc_flags) & (IFF_ALLMULTI | IFF_MULTICAST | IFF_PROMISC)) != 0) {
303217214Sjmallett		flags = 0;
304217214Sjmallett		if ((ifp->if_flags & IFF_ALLMULTI) != 0)
305217214Sjmallett			flags |= CVMX_IFF_ALLMULTI;
306217214Sjmallett		if ((ifp->if_flags & IFF_PROMISC) != 0)
307217214Sjmallett			flags |= CVMX_IFF_PROMISC;
308217214Sjmallett		cvmx_mgmt_port_set_multicast_list(sc->sc_port, flags);
309217214Sjmallett	}
310217214Sjmallett
311217214Sjmallett	mac = 0;
312217214Sjmallett	memcpy((u_int8_t *)&mac + 2, IF_LLADDR(ifp), 6);
313217214Sjmallett	cvmx_mgmt_port_set_mac(sc->sc_port, mac);
314217214Sjmallett
315217214Sjmallett	/* XXX link state?  */
316217214Sjmallett
317217214Sjmallett	if ((ifp->if_flags & IFF_UP) != 0)
318217214Sjmallett		cvmx_mgmt_port_enable(sc->sc_port);
319217214Sjmallett
320217214Sjmallett	ifp->if_drv_flags |= IFF_DRV_RUNNING;
321217214Sjmallett	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
322217214Sjmallett}
323217214Sjmallett
324217214Sjmallettstatic int
325217214Sjmallettoctm_transmit(struct ifnet *ifp, struct mbuf *m)
326217214Sjmallett{
327217214Sjmallett	struct octm_softc *sc;
328217214Sjmallett	cvmx_mgmt_port_result_t result;
329217214Sjmallett
330217214Sjmallett	sc = ifp->if_softc;
331217214Sjmallett
332217214Sjmallett	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
333217214Sjmallett	    IFF_DRV_RUNNING) {
334217214Sjmallett		m_freem(m);
335217214Sjmallett		return (0);
336217214Sjmallett	}
337217214Sjmallett
338217214Sjmallett	result = cvmx_mgmt_port_sendm(sc->sc_port, m);
339217214Sjmallett
340217244Sjmallett	if (result == CVMX_MGMT_PORT_SUCCESS) {
341217214Sjmallett		ETHER_BPF_MTAP(ifp, m);
342217244Sjmallett
343217244Sjmallett		ifp->if_opackets++;
344217244Sjmallett		ifp->if_obytes += m->m_pkthdr.len;
345217244Sjmallett	} else
346217214Sjmallett		ifp->if_oerrors++;
347217214Sjmallett
348217214Sjmallett	m_freem(m);
349217214Sjmallett
350217214Sjmallett	switch (result) {
351217214Sjmallett	case CVMX_MGMT_PORT_SUCCESS:
352217214Sjmallett		return (0);
353217214Sjmallett	case CVMX_MGMT_PORT_NO_MEMORY:
354217214Sjmallett		return (ENOBUFS);
355217214Sjmallett	case CVMX_MGMT_PORT_INVALID_PARAM:
356217214Sjmallett		return (ENXIO);
357217214Sjmallett	case CVMX_MGMT_PORT_INIT_ERROR:
358217214Sjmallett		return (EIO);
359217214Sjmallett	default:
360217214Sjmallett		return (EDOOFUS);
361217214Sjmallett	}
362217214Sjmallett}
363217214Sjmallett
364217214Sjmallettstatic int
365217214Sjmallettoctm_medchange(struct ifnet *ifp)
366217214Sjmallett{
367217214Sjmallett	return (ENOTSUP);
368217214Sjmallett}
369217214Sjmallett
370217214Sjmallettstatic void
371217214Sjmallettoctm_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
372217214Sjmallett{
373217214Sjmallett	struct octm_softc *sc;
374217214Sjmallett	cvmx_helper_link_info_t link_info;
375217214Sjmallett
376217214Sjmallett	sc = ifp->if_softc;
377217214Sjmallett
378217214Sjmallett	ifm->ifm_status = IFM_AVALID;
379217214Sjmallett	ifm->ifm_active = IFT_ETHER;
380217214Sjmallett
381217214Sjmallett	link_info = cvmx_mgmt_port_link_get(sc->sc_port);
382217214Sjmallett	if (!link_info.s.link_up)
383217214Sjmallett		return;
384217214Sjmallett
385217214Sjmallett	ifm->ifm_status |= IFM_ACTIVE;
386217214Sjmallett
387217214Sjmallett	switch (link_info.s.speed) {
388217214Sjmallett	case 10:
389217214Sjmallett		ifm->ifm_active |= IFM_10_T;
390217214Sjmallett		break;
391217214Sjmallett	case 100:
392217214Sjmallett		ifm->ifm_active |= IFM_100_TX;
393217214Sjmallett		break;
394217214Sjmallett	case 1000:
395217214Sjmallett		ifm->ifm_active |= IFM_1000_T;
396217214Sjmallett		break;
397217214Sjmallett	case 10000:
398217214Sjmallett		ifm->ifm_active |= IFM_10G_T;
399217214Sjmallett		break;
400217214Sjmallett	}
401217214Sjmallett
402217214Sjmallett	if (link_info.s.full_duplex)
403217214Sjmallett		ifm->ifm_active |= IFM_FDX;
404217214Sjmallett	else
405217214Sjmallett		ifm->ifm_active |= IFM_HDX;
406217214Sjmallett}
407217214Sjmallett
408217214Sjmallettstatic int
409217214Sjmallettoctm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
410217214Sjmallett{
411217214Sjmallett	struct octm_softc *sc;
412217214Sjmallett	struct ifreq *ifr;
413217214Sjmallett#ifdef INET
414217214Sjmallett	struct ifaddr *ifa;
415217214Sjmallett#endif
416217214Sjmallett	int error;
417217214Sjmallett
418217214Sjmallett	sc = ifp->if_softc;
419217214Sjmallett	ifr = (struct ifreq *)data;
420217214Sjmallett#ifdef INET
421217214Sjmallett	ifa = (struct ifaddr *)data;
422217214Sjmallett#endif
423217214Sjmallett
424217214Sjmallett	switch (cmd) {
425217214Sjmallett	case SIOCSIFADDR:
426217214Sjmallett#ifdef INET
427217214Sjmallett		/*
428217214Sjmallett		 * Avoid reinitialization unless it's necessary.
429217214Sjmallett		 */
430217214Sjmallett		if (ifa->ifa_addr->sa_family == AF_INET) {
431217214Sjmallett			ifp->if_flags |= IFF_UP;
432217214Sjmallett			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
433217214Sjmallett				octm_init(sc);
434217214Sjmallett			arp_ifinit(ifp, ifa);
435217214Sjmallett
436217214Sjmallett			return (0);
437217214Sjmallett		}
438217214Sjmallett#endif
439217214Sjmallett		error = ether_ioctl(ifp, cmd, data);
440217214Sjmallett		if (error != 0)
441217214Sjmallett			return (error);
442217214Sjmallett		return (0);
443217214Sjmallett
444217214Sjmallett	case SIOCSIFFLAGS:
445217214Sjmallett		if (ifp->if_flags == sc->sc_flags)
446217214Sjmallett			return (0);
447217214Sjmallett		if ((ifp->if_flags & IFF_UP) != 0) {
448217214Sjmallett			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
449217214Sjmallett				octm_init(sc);
450217214Sjmallett		} else {
451217214Sjmallett			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
452217214Sjmallett				cvmx_mgmt_port_disable(sc->sc_port);
453217214Sjmallett
454217214Sjmallett				ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
455217214Sjmallett			}
456217214Sjmallett		}
457217214Sjmallett		sc->sc_flags = ifp->if_flags;
458217214Sjmallett		return (0);
459217214Sjmallett
460217214Sjmallett	case SIOCSIFCAP:
461217214Sjmallett		/*
462217214Sjmallett		 * Just change the capabilities in software, currently none
463217214Sjmallett		 * require reprogramming hardware, they just toggle whether we
464217214Sjmallett		 * make use of already-present facilities in software.
465217214Sjmallett		 */
466217214Sjmallett		ifp->if_capenable = ifr->ifr_reqcap;
467217214Sjmallett		return (0);
468217214Sjmallett
469217214Sjmallett	case SIOCSIFMTU:
470217214Sjmallett		cvmx_mgmt_port_set_max_packet_size(sc->sc_port, ifr->ifr_mtu + ifp->if_data.ifi_hdrlen);
471217214Sjmallett		return (0);
472217214Sjmallett
473217214Sjmallett	case SIOCSIFMEDIA:
474217214Sjmallett	case SIOCGIFMEDIA:
475217214Sjmallett		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
476217214Sjmallett		if (error != 0)
477217214Sjmallett			return (error);
478217214Sjmallett		return (0);
479217214Sjmallett
480217214Sjmallett	default:
481217214Sjmallett		error = ether_ioctl(ifp, cmd, data);
482217214Sjmallett		if (error != 0)
483217214Sjmallett			return (error);
484217214Sjmallett		return (0);
485217214Sjmallett	}
486217214Sjmallett}
487217214Sjmallett
488217214Sjmallettstatic void
489217214Sjmallettoctm_rx_intr(void *arg)
490217214Sjmallett{
491217214Sjmallett	struct octm_softc *sc = arg;
492217214Sjmallett	cvmx_mixx_isr_t mixx_isr;
493217214Sjmallett	int len;
494217214Sjmallett
495217214Sjmallett	mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
496217214Sjmallett	if (!mixx_isr.s.irthresh) {
497217214Sjmallett		device_printf(sc->sc_dev, "stray interrupt.\n");
498217214Sjmallett		return;
499217214Sjmallett	}
500217214Sjmallett
501217214Sjmallett	for (;;) {
502217214Sjmallett		struct mbuf *m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
503217214Sjmallett		if (m == NULL) {
504217214Sjmallett			device_printf(sc->sc_dev, "no memory for receive mbuf.\n");
505217214Sjmallett			return;
506217214Sjmallett		}
507217214Sjmallett
508217214Sjmallett
509217214Sjmallett		len = cvmx_mgmt_port_receive(sc->sc_port, MCLBYTES, m->m_data);
510217214Sjmallett		if (len > 0) {
511217214Sjmallett			m->m_pkthdr.rcvif = sc->sc_ifp;
512217214Sjmallett			m->m_pkthdr.len = m->m_len = len;
513217214Sjmallett
514217214Sjmallett			sc->sc_ifp->if_ipackets++;
515217214Sjmallett
516217214Sjmallett			(*sc->sc_ifp->if_input)(sc->sc_ifp, m);
517217214Sjmallett
518217214Sjmallett			continue;
519217214Sjmallett		}
520217214Sjmallett
521217222Sjmallett		m_freem(m);
522217222Sjmallett
523217214Sjmallett		if (len == 0)
524217214Sjmallett			break;
525217214Sjmallett
526217214Sjmallett		sc->sc_ifp->if_ierrors++;
527217214Sjmallett	}
528217222Sjmallett
529217222Sjmallett	/* Acknowledge interrupts.  */
530217222Sjmallett	cvmx_write_csr(CVMX_MIXX_ISR(sc->sc_port), mixx_isr.u64);
531217222Sjmallett	cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
532217214Sjmallett}
533