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