1 /*************************************************************************
2 Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above
14 copyright notice, this list of conditions and the following
15 disclaimer in the documentation and/or other materials provided
16 with the distribution.
18 * Neither the name of Cavium Networks nor the names of
19 its contributors may be used to endorse or promote products
20 derived from this software without specific prior written
23 This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
25 TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
26 AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
27 *************************************************************************/
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
36 #include <sys/endian.h>
37 #include <sys/kernel.h>
40 #include <sys/socket.h>
41 #include <sys/module.h>
43 #include <sys/taskqueue.h>
45 #include <net/ethernet.h>
47 #include <net/if_types.h>
49 #include "wrapper-cvmx-includes.h"
50 #include "ethernet-headers.h"
52 #include "octebusvar.h"
56 * Convert 0444 to tunables, 0644 to sysctls.
58 #if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
59 int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
61 int num_packet_buffers = 1024;
63 TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
65 "\t\tNumber of packet buffers to allocate and store in the\n"
66 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
67 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
69 int pow_receive_group = 15;
70 TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
72 "\t\tPOW group to receive packets from. All ethernet hardware\n"
73 "\t\twill be configured to send incomming packets to this POW\n"
74 "\t\tgroup. Also any other software can submit packets to this\n"
75 "\t\tgroup for the kernel to process." */
77 extern int octeon_is_simulation(void);
80 * Exported from the kernel so we can determine board information. It is
81 * passed by the bootloader to the kernel.
83 extern cvmx_bootinfo_t *octeon_bootinfo;
86 * Periodic timer to check auto negotiation
88 static struct callout cvm_oct_poll_timer;
91 * Array of every ethernet device owned by this driver indexed by
92 * the ipd input port number.
94 struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
97 * Task to handle link status changes.
99 static struct taskqueue *cvm_oct_link_taskq;
102 * Function to update link status.
104 static void cvm_oct_update_link(void *context, int pending)
106 cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
107 struct ifnet *ifp = priv->ifp;
108 cvmx_helper_link_info_t link_info;
110 link_info.u64 = priv->link_info;
112 if (link_info.s.link_up) {
113 if_link_state_change(ifp, LINK_STATE_UP);
114 DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
115 if_name(ifp), link_info.s.speed,
116 (link_info.s.full_duplex) ? "Full" : "Half",
117 priv->port, priv->queue);
119 if_link_state_change(ifp, LINK_STATE_DOWN);
120 DEBUGPRINT("%s: Link down\n", if_name(ifp));
122 priv->need_link_update = 0;
126 * Periodic timer tick for slow management operations
128 * @param arg Device to check
130 static void cvm_do_timer(void *arg)
134 if (port < CVMX_PIP_NUM_INPUT_PORTS) {
135 if (cvm_oct_device[port]) {
138 cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
141 /* skip polling if we don't get the lock */
142 if (MDIO_TRYLOCK()) {
143 priv->poll(cvm_oct_device[port]);
146 if (priv->need_link_update) {
148 taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
153 queues_per_port = cvmx_pko_get_num_queues(port);
154 /* Drain any pending packets in the free list */
155 for (qos = 0; qos < queues_per_port; qos++) {
156 if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
157 IF_LOCK(&priv->tx_free_queue[qos]);
158 while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
161 _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
164 IF_UNLOCK(&priv->tx_free_queue[qos]);
169 priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
174 /* Poll the next port in a 50th of a second.
175 This spreads the polling of ports out a little bit */
176 callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
179 /* If any updates were made in this run, continue iterating at
180 * 1/50th of a second, so that if a link has merely gone down
181 * temporarily (e.g. because of interface reinitialization) it
182 * will not be forced to stay down for an entire second.
186 callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
188 /* All ports have been polled. Start the next iteration through
189 the ports in one second */
190 callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
197 * Configure common hardware for all interfaces
199 static void cvm_oct_configure_common_hw(device_t bus)
201 struct octebus_softc *sc;
205 sc = device_get_softc(bus);
209 cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
210 cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
211 if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
212 cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
215 cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
217 /* Enable the MII interface */
218 if (!octeon_is_simulation())
219 cvmx_write_csr(CVMX_SMI_EN, 1);
221 /* Register an IRQ hander for to receive POW interrupts */
223 sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
224 CVMX_IRQ_WORKQ0 + pow_receive_group,
225 CVMX_IRQ_WORKQ0 + pow_receive_group,
227 if (sc->sc_rx_irq == NULL) {
228 device_printf(bus, "could not allocate workq irq");
232 error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
233 cvm_oct_do_interrupt, NULL, cvm_oct_device,
236 device_printf(bus, "could not setup workq irq");
242 if (USE_MULTICORE_RECEIVE) {
246 for (cpu = 0; cpu < mp_maxid; cpu++) {
247 if (!CPU_ABSENT(cpu) &&
248 (cpu != PCPU_GET(cpuid))) {
250 en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
251 en.s.workq |= (1<<pow_receive_group);
252 cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
263 * Free a work queue entry received in a intercept callback.
265 * @param work_queue_entry
266 * Work queue entry to free
267 * @return Zero on success, Negative on failure.
269 int cvm_oct_free_work(void *work_queue_entry)
271 cvmx_wqe_t *work = work_queue_entry;
273 int segments = work->word2.s.bufs;
274 cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
277 cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
278 if (__predict_false(!segment_ptr.s.i))
279 cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
280 segment_ptr = next_ptr;
282 cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
289 * Module/ driver initialization. Creates the linux network
292 * @return Zero on success
294 int cvm_oct_init_module(device_t bus)
300 int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
303 printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
305 cvm_oct_rx_initialize();
306 cvm_oct_configure_common_hw(bus);
308 cvmx_helper_initialize_packet_io_global();
310 /* Change the input group for all ports before input is enabled */
311 num_interfaces = cvmx_helper_get_number_of_interfaces();
312 for (interface = 0; interface < num_interfaces; interface++) {
313 int num_ports = cvmx_helper_ports_on_interface(interface);
316 for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
317 cvmx_pip_prt_tagx_t pip_prt_tagx;
318 pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
319 pip_prt_tagx.s.grp = pow_receive_group;
320 cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
324 cvmx_helper_ipd_and_packet_input_enable();
326 memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
328 cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
329 taskqueue_thread_enqueue, &cvm_oct_link_taskq);
330 taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
333 /* Initialize the FAU used for counting packet buffers that need to be freed */
334 cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
337 num_interfaces = cvmx_helper_get_number_of_interfaces();
338 for (interface = 0; interface < num_interfaces; interface++) {
339 cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
340 int num_ports = cvmx_helper_ports_on_interface(interface);
343 for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
344 cvm_oct_private_t *priv;
347 dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
349 ifp = if_alloc(IFT_ETHER);
350 if (dev == NULL || ifp == NULL) {
351 printf("\t\tFailed to allocate ethernet device for port %d\n", port);
355 /* Initialize the device private structure. */
357 priv = device_get_softc(dev);
362 priv->queue = cvmx_pko_get_base_queue(priv->port);
363 priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
364 for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
365 cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
366 TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
368 switch (priv->imode) {
370 /* These types don't support ports to IPD/PKO */
371 case CVMX_HELPER_INTERFACE_MODE_DISABLED:
372 case CVMX_HELPER_INTERFACE_MODE_PCIE:
373 case CVMX_HELPER_INTERFACE_MODE_PICMG:
376 case CVMX_HELPER_INTERFACE_MODE_NPI:
377 priv->init = cvm_oct_common_init;
378 priv->uninit = cvm_oct_common_uninit;
379 device_set_desc(dev, "Cavium Octeon NPI Ethernet");
382 case CVMX_HELPER_INTERFACE_MODE_XAUI:
383 priv->init = cvm_oct_xaui_init;
384 priv->uninit = cvm_oct_common_uninit;
385 device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
388 case CVMX_HELPER_INTERFACE_MODE_LOOP:
389 priv->init = cvm_oct_common_init;
390 priv->uninit = cvm_oct_common_uninit;
391 device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
394 case CVMX_HELPER_INTERFACE_MODE_SGMII:
395 priv->init = cvm_oct_sgmii_init;
396 priv->uninit = cvm_oct_common_uninit;
397 device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
400 case CVMX_HELPER_INTERFACE_MODE_SPI:
401 priv->init = cvm_oct_spi_init;
402 priv->uninit = cvm_oct_spi_uninit;
403 device_set_desc(dev, "Cavium Octeon SPI Ethernet");
406 case CVMX_HELPER_INTERFACE_MODE_RGMII:
407 priv->init = cvm_oct_rgmii_init;
408 priv->uninit = cvm_oct_rgmii_uninit;
409 device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
412 case CVMX_HELPER_INTERFACE_MODE_GMII:
413 priv->init = cvm_oct_rgmii_init;
414 priv->uninit = cvm_oct_rgmii_uninit;
415 device_set_desc(dev, "Cavium Octeon GMII Ethernet");
419 ifp->if_softc = priv;
422 panic("%s: unsupported device type, need to free ifp.", __func__);
424 if (priv->init(ifp) < 0) {
425 printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
426 interface, priv->port);
427 panic("%s: init failed, need to free ifp.", __func__);
429 cvm_oct_device[priv->port] = ifp;
430 fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
435 if (INTERRUPT_LIMIT) {
436 /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
437 cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
439 /* Enable POW timer interrupt. It will count when there are packets available */
440 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
442 /* Enable POW interrupt when our port has at least one packet */
443 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
446 callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
447 callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
454 * Module / driver shutdown
456 * @return Zero on success
458 void cvm_oct_cleanup_module(void)
462 /* Disable POW interrupt */
463 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
466 /* Free the interrupt handler */
467 free_irq(8 + pow_receive_group, cvm_oct_device);
470 callout_stop(&cvm_oct_poll_timer);
471 cvm_oct_rx_shutdown();
473 cvmx_helper_shutdown_packet_io_global();
475 /* Free the ethernet devices */
476 for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
477 if (cvm_oct_device[port]) {
478 cvm_oct_tx_shutdown(cvm_oct_device[port]);
480 unregister_netdev(cvm_oct_device[port]);
481 kfree(cvm_oct_device[port]);
483 panic("%s: need to detach and free interface.", __func__);
485 cvm_oct_device[port] = NULL;