]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/mips/cavium/if_octm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / mips / cavium / if_octm.c
1 /*-
2  * Copyright (c) 2010-2011 Juli Mallett <jmallett@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Cavium Octeon management port Ethernet devices.
31  */
32
33 #include "opt_inet.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
40 #include <sys/mbuf.h>
41 #include <sys/lock.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/rman.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #include <sys/sysctl.h>
48
49 #include <net/bpf.h>
50 #include <net/ethernet.h>
51 #include <net/if.h>
52 #include <net/if_dl.h>
53 #include <net/if_media.h>
54 #include <net/if_types.h>
55 #include <net/if_var.h>
56 #include <net/if_vlan_var.h>
57
58 #ifdef INET
59 #include <netinet/in.h>
60 #include <netinet/if_ether.h>
61 #endif
62
63 #include <contrib/octeon-sdk/cvmx.h>
64 #include <mips/cavium/octeon_irq.h>
65 #include <contrib/octeon-sdk/cvmx-mgmt-port.h>
66
67 struct octm_softc {
68         struct ifnet *sc_ifp;
69         device_t sc_dev;
70         unsigned sc_port;
71         int sc_flags;
72         struct ifmedia sc_ifmedia;
73         struct resource *sc_intr;
74         void *sc_intr_cookie;
75 };
76
77 static void     octm_identify(driver_t *, device_t);
78 static int      octm_probe(device_t);
79 static int      octm_attach(device_t);
80 static int      octm_detach(device_t);
81 static int      octm_shutdown(device_t);
82
83 static void     octm_init(void *);
84 static int      octm_transmit(struct ifnet *, struct mbuf *);
85
86 static int      octm_medchange(struct ifnet *);
87 static void     octm_medstat(struct ifnet *, struct ifmediareq *);
88
89 static int      octm_ioctl(struct ifnet *, u_long, caddr_t);
90
91 static void     octm_rx_intr(void *);
92
93 static device_method_t octm_methods[] = {
94         /* Device interface */
95         DEVMETHOD(device_identify,      octm_identify),
96         DEVMETHOD(device_probe,         octm_probe),
97         DEVMETHOD(device_attach,        octm_attach),
98         DEVMETHOD(device_detach,        octm_detach),
99         DEVMETHOD(device_shutdown,      octm_shutdown),
100
101         { 0, 0 }
102 };
103
104 static driver_t octm_driver = {
105         "octm",
106         octm_methods,
107         sizeof (struct octm_softc),
108 };
109
110 static devclass_t octm_devclass;
111
112 DRIVER_MODULE(octm, ciu, octm_driver, octm_devclass, 0, 0);
113
114 static void
115 octm_identify(driver_t *drv, device_t parent)
116 {
117         unsigned i;
118
119         if (!octeon_has_feature(OCTEON_FEATURE_MGMT_PORT))
120                 return;
121
122         for (i = 0; i < CVMX_MGMT_PORT_NUM_PORTS; i++)
123                 BUS_ADD_CHILD(parent, 0, "octm", i);
124 }
125
126 static int
127 octm_probe(device_t dev)
128 {
129         cvmx_mgmt_port_result_t result;
130
131         result = cvmx_mgmt_port_initialize(device_get_unit(dev));
132         switch (result) {
133         case CVMX_MGMT_PORT_SUCCESS:
134                 break;
135         case CVMX_MGMT_PORT_NO_MEMORY:
136                 return (ENOBUFS);
137         case CVMX_MGMT_PORT_INVALID_PARAM:
138                 return (ENXIO);
139         case CVMX_MGMT_PORT_INIT_ERROR:
140                 return (EIO);
141         }
142
143         device_set_desc(dev, "Cavium Octeon Management Ethernet");
144
145         return (0);
146 }
147
148 static int
149 octm_attach(device_t dev)
150 {
151         struct ifnet *ifp;
152         struct octm_softc *sc;
153         cvmx_mixx_irhwm_t mixx_irhwm;
154         cvmx_mixx_intena_t mixx_intena;
155         uint64_t mac;
156         int error;
157         int irq;
158         int rid;
159
160         sc = device_get_softc(dev);
161         sc->sc_dev = dev;
162         sc->sc_port = device_get_unit(dev);
163
164         switch (sc->sc_port) {
165         case 0:
166                 irq = OCTEON_IRQ_MII;
167                 break;
168         case 1:
169                 irq = OCTEON_IRQ_MII1;
170                 break;
171         default:
172                 device_printf(dev, "unsupported management port %u.\n", sc->sc_port);
173                 return (ENXIO);
174         }
175
176         /*
177          * Set MAC address for this management port.
178          */
179         mac = 0;
180         memcpy((u_int8_t *)&mac + 2, cvmx_sysinfo_get()->mac_addr_base, 6);
181         mac += sc->sc_port;
182
183         cvmx_mgmt_port_set_mac(sc->sc_port, mac);
184
185         /* No watermark for input ring.  */
186         mixx_irhwm.u64 = 0;
187         cvmx_write_csr(CVMX_MIXX_IRHWM(sc->sc_port), mixx_irhwm.u64);
188
189         /* Enable input ring interrupts.  */
190         mixx_intena.u64 = 0;
191         mixx_intena.s.ithena = 1;
192         cvmx_write_csr(CVMX_MIXX_INTENA(sc->sc_port), mixx_intena.u64);
193
194         /* Allocate and establish interrupt.  */
195         rid = 0;
196         sc->sc_intr = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid,
197             irq, irq, 1, RF_ACTIVE);
198         if (sc->sc_intr == NULL) {
199                 device_printf(dev, "unable to allocate IRQ.\n");
200                 return (ENXIO);
201         }
202
203         error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL,
204             octm_rx_intr, sc, &sc->sc_intr_cookie);
205         if (error != 0) {
206                 device_printf(dev, "unable to setup interrupt.\n");
207                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
208                 return (ENXIO);
209         }
210
211         bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx");
212
213         /* XXX Possibly should enable TX interrupts.  */
214
215         ifp = if_alloc(IFT_ETHER);
216         if (ifp == NULL) {
217                 device_printf(dev, "cannot allocate ifnet.\n");
218                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
219                 return (ENOMEM);
220         }
221
222         if_initname(ifp, device_get_name(dev), device_get_unit(dev));
223         ifp->if_mtu = ETHERMTU;
224         ifp->if_init = octm_init;
225         ifp->if_softc = sc;
226         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
227         ifp->if_ioctl = octm_ioctl;
228
229         sc->sc_ifp = ifp;
230         sc->sc_flags = ifp->if_flags;
231
232         ifmedia_init(&sc->sc_ifmedia, 0, octm_medchange, octm_medstat);
233
234         ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
235         ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
236
237         ether_ifattach(ifp, (const u_int8_t *)&mac + 2);
238
239         ifp->if_transmit = octm_transmit;
240
241         ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
242         ifp->if_capabilities = IFCAP_VLAN_MTU;
243         ifp->if_capenable = ifp->if_capabilities;
244
245         IFQ_SET_MAXLEN(&ifp->if_snd, CVMX_MGMT_PORT_NUM_TX_BUFFERS);
246         ifp->if_snd.ifq_drv_maxlen = CVMX_MGMT_PORT_NUM_TX_BUFFERS;
247         IFQ_SET_READY(&ifp->if_snd);
248
249         return (bus_generic_attach(dev));
250 }
251
252 static int
253 octm_detach(device_t dev)
254 {
255         struct octm_softc *sc;
256         cvmx_mgmt_port_result_t result;
257
258         sc = device_get_softc(dev);
259
260         result = cvmx_mgmt_port_initialize(sc->sc_port);
261         switch (result) {
262         case CVMX_MGMT_PORT_SUCCESS:
263                 break;
264         case CVMX_MGMT_PORT_NO_MEMORY:
265                 return (ENOBUFS);
266         case CVMX_MGMT_PORT_INVALID_PARAM:
267                 return (ENXIO);
268         case CVMX_MGMT_PORT_INIT_ERROR:
269                 return (EIO);
270         }
271
272         bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
273         /* XXX Incomplete.  */
274
275         return (0);
276 }
277
278 static int
279 octm_shutdown(device_t dev)
280 {
281         return (octm_detach(dev));
282 }
283
284 static void
285 octm_init(void *arg)
286 {
287         struct ifnet *ifp;
288         struct octm_softc *sc;
289         cvmx_mgmt_port_netdevice_flags_t flags;
290         uint64_t mac;
291
292         sc = arg;
293         ifp = sc->sc_ifp;
294
295         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
296                 cvmx_mgmt_port_disable(sc->sc_port);
297
298                 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
299         }
300
301         /*
302          * NB:
303          * MAC must be set before allmulti and promisc below, as
304          * cvmx_mgmt_port_set_mac will always enable the CAM, and turning on
305          * promiscuous mode only works with the CAM disabled.
306          */
307         mac = 0;
308         memcpy((u_int8_t *)&mac + 2, IF_LLADDR(ifp), 6);
309         cvmx_mgmt_port_set_mac(sc->sc_port, mac);
310
311         /*
312          * This is done unconditionally, rather than only if sc_flags have
313          * changed because of set_mac's effect on the CAM noted above.
314          */
315         flags = 0;
316         if ((ifp->if_flags & IFF_ALLMULTI) != 0)
317                 flags |= CVMX_IFF_ALLMULTI;
318         if ((ifp->if_flags & IFF_PROMISC) != 0)
319                 flags |= CVMX_IFF_PROMISC;
320         cvmx_mgmt_port_set_multicast_list(sc->sc_port, flags);
321
322         /* XXX link state?  */
323
324         if ((ifp->if_flags & IFF_UP) != 0)
325                 cvmx_mgmt_port_enable(sc->sc_port);
326
327         ifp->if_drv_flags |= IFF_DRV_RUNNING;
328         ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
329 }
330
331 static int
332 octm_transmit(struct ifnet *ifp, struct mbuf *m)
333 {
334         struct octm_softc *sc;
335         cvmx_mgmt_port_result_t result;
336
337         sc = ifp->if_softc;
338
339         if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
340             IFF_DRV_RUNNING) {
341                 m_freem(m);
342                 return (0);
343         }
344
345         result = cvmx_mgmt_port_sendm(sc->sc_port, m);
346
347         if (result == CVMX_MGMT_PORT_SUCCESS) {
348                 ETHER_BPF_MTAP(ifp, m);
349
350                 ifp->if_opackets++;
351                 ifp->if_obytes += m->m_pkthdr.len;
352         } else
353                 ifp->if_oerrors++;
354
355         m_freem(m);
356
357         switch (result) {
358         case CVMX_MGMT_PORT_SUCCESS:
359                 return (0);
360         case CVMX_MGMT_PORT_NO_MEMORY:
361                 return (ENOBUFS);
362         case CVMX_MGMT_PORT_INVALID_PARAM:
363                 return (ENXIO);
364         case CVMX_MGMT_PORT_INIT_ERROR:
365                 return (EIO);
366         default:
367                 return (EDOOFUS);
368         }
369 }
370
371 static int
372 octm_medchange(struct ifnet *ifp)
373 {
374         return (ENOTSUP);
375 }
376
377 static void
378 octm_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
379 {
380         struct octm_softc *sc;
381         cvmx_helper_link_info_t link_info;
382
383         sc = ifp->if_softc;
384
385         ifm->ifm_status = IFM_AVALID;
386         ifm->ifm_active = IFT_ETHER;
387
388         link_info = cvmx_mgmt_port_link_get(sc->sc_port);
389         if (!link_info.s.link_up)
390                 return;
391
392         ifm->ifm_status |= IFM_ACTIVE;
393
394         switch (link_info.s.speed) {
395         case 10:
396                 ifm->ifm_active |= IFM_10_T;
397                 break;
398         case 100:
399                 ifm->ifm_active |= IFM_100_TX;
400                 break;
401         case 1000:
402                 ifm->ifm_active |= IFM_1000_T;
403                 break;
404         case 10000:
405                 ifm->ifm_active |= IFM_10G_T;
406                 break;
407         }
408
409         if (link_info.s.full_duplex)
410                 ifm->ifm_active |= IFM_FDX;
411         else
412                 ifm->ifm_active |= IFM_HDX;
413 }
414
415 static int
416 octm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
417 {
418         struct octm_softc *sc;
419         struct ifreq *ifr;
420 #ifdef INET
421         struct ifaddr *ifa;
422 #endif
423         int error;
424
425         sc = ifp->if_softc;
426         ifr = (struct ifreq *)data;
427 #ifdef INET
428         ifa = (struct ifaddr *)data;
429 #endif
430
431         switch (cmd) {
432         case SIOCSIFADDR:
433 #ifdef INET
434                 /*
435                  * Avoid reinitialization unless it's necessary.
436                  */
437                 if (ifa->ifa_addr->sa_family == AF_INET) {
438                         ifp->if_flags |= IFF_UP;
439                         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
440                                 octm_init(sc);
441                         arp_ifinit(ifp, ifa);
442
443                         return (0);
444                 }
445 #endif
446                 error = ether_ioctl(ifp, cmd, data);
447                 if (error != 0)
448                         return (error);
449                 return (0);
450
451         case SIOCSIFFLAGS:
452                 if (ifp->if_flags == sc->sc_flags)
453                         return (0);
454                 if ((ifp->if_flags & IFF_UP) != 0) {
455                         octm_init(sc);
456                 } else {
457                         if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
458                                 cvmx_mgmt_port_disable(sc->sc_port);
459
460                                 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
461                         }
462                 }
463                 sc->sc_flags = ifp->if_flags;
464                 return (0);
465         
466         case SIOCSIFCAP:
467                 /*
468                  * Just change the capabilities in software, currently none
469                  * require reprogramming hardware, they just toggle whether we
470                  * make use of already-present facilities in software.
471                  */
472                 ifp->if_capenable = ifr->ifr_reqcap;
473                 return (0);
474
475         case SIOCSIFMTU:
476                 cvmx_mgmt_port_set_max_packet_size(sc->sc_port, ifr->ifr_mtu + ifp->if_data.ifi_hdrlen);
477                 return (0);
478
479         case SIOCSIFMEDIA:
480         case SIOCGIFMEDIA:
481                 error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
482                 if (error != 0)
483                         return (error);
484                 return (0);
485         
486         default:
487                 error = ether_ioctl(ifp, cmd, data);
488                 if (error != 0)
489                         return (error);
490                 return (0);
491         }
492 }
493
494 static void
495 octm_rx_intr(void *arg)
496 {
497         struct octm_softc *sc = arg;
498         cvmx_mixx_isr_t mixx_isr;
499         int len;
500
501         mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
502         if (!mixx_isr.s.irthresh) {
503                 device_printf(sc->sc_dev, "stray interrupt.\n");
504                 return;
505         }
506
507         for (;;) {
508                 struct mbuf *m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
509                 if (m == NULL) {
510                         device_printf(sc->sc_dev, "no memory for receive mbuf.\n");
511                         return;
512                 }
513
514
515                 len = cvmx_mgmt_port_receive(sc->sc_port, MCLBYTES, m->m_data);
516                 if (len > 0) {
517                         m->m_pkthdr.rcvif = sc->sc_ifp;
518                         m->m_pkthdr.len = m->m_len = len;
519
520                         sc->sc_ifp->if_ipackets++;
521
522                         (*sc->sc_ifp->if_input)(sc->sc_ifp, m);
523
524                         continue;
525                 }
526
527                 m_freem(m);
528
529                 if (len == 0)
530                         break;
531
532                 sc->sc_ifp->if_ierrors++;
533         }
534
535         /* Acknowledge interrupts.  */
536         cvmx_write_csr(CVMX_MIXX_ISR(sc->sc_port), mixx_isr.u64);
537         cvmx_read_csr(CVMX_MIXX_ISR(sc->sc_port));
538 }