]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/mips/cavium/octe/ethernet.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / mips / cavium / octe / ethernet.c
1 /*************************************************************************
2 Copyright (c) 2003-2007  Cavium Networks (support@cavium.com). All rights
3 reserved.
4
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are
8 met:
9
10     * Redistributions of source code must retain the above copyright
11       notice, this list of conditions and the following disclaimer.
12
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.
17
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
21       permission.
22
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.
24
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 *************************************************************************/
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/conf.h>
36 #include <sys/endian.h>
37 #include <sys/kernel.h>
38 #include <sys/rman.h>
39 #include <sys/mbuf.h>
40 #include <sys/socket.h>
41 #include <sys/module.h>
42 #include <sys/smp.h>
43 #include <sys/taskqueue.h>
44
45 #include <net/ethernet.h>
46 #include <net/if.h>
47 #include <net/if_types.h>
48
49 #include "wrapper-cvmx-includes.h"
50 #include "ethernet-headers.h"
51
52 #include "octebusvar.h"
53
54 /*
55  * XXX/juli
56  * Convert 0444 to tunables, 0644 to sysctls.
57  */
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;
60 #else
61 int num_packet_buffers = 1024;
62 #endif
63 TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
64 /*
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." */
68
69 int pow_receive_group = 15;
70 TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
71 /*
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." */
76
77 int pow_send_group = -1; /* XXX Should be a sysctl.  */
78 TUNABLE_INT("hw.octe.pow_send_group", &pow_send_group);
79 /*
80                  "\t\tPOW group to send packets to other software on. This\n"
81                  "\t\tcontrols the creation of the virtual device pow0.\n"
82                  "\t\talways_use_pow also depends on this value." */
83
84 int always_use_pow;
85 TUNABLE_INT("hw.octe.always_use_pow", &always_use_pow);
86 /*
87                  "\t\tWhen set, always send to the pow group. This will cause\n"
88                  "\t\tpackets sent to real ethernet devices to be sent to the\n"
89                  "\t\tPOW group instead of the hardware. Unless some other\n"
90                  "\t\tapplication changes the config, packets will still be\n"
91                  "\t\treceived from the low level hardware. Use this option\n"
92                  "\t\tto allow a CVMX app to intercept all packets from the\n"
93                  "\t\tlinux kernel. You must specify pow_send_group along with\n"
94                  "\t\tthis option." */
95
96 char pow_send_list[128] = "";
97 TUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list);
98 /*
99                  "\t\tComma separated list of ethernet devices that should use the\n"
100                  "\t\tPOW for transmit instead of the actual ethernet hardware. This\n"
101                  "\t\tis a per port version of always_use_pow. always_use_pow takes\n"
102                  "\t\tprecedence over this list. For example, setting this to\n"
103                  "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
104                  "\t\tusing the pow_send_group." */
105
106
107 static int disable_core_queueing = 1;
108 TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
109 /*
110                 "\t\tWhen set the networking core's tx_queue_len is set to zero.  This\n"
111                 "\t\tallows packets to be sent without lock contention in the packet scheduler\n"
112                 "\t\tresulting in some cases in improved throughput.\n" */
113
114 extern int octeon_is_simulation(void);
115
116 /**
117  * Exported from the kernel so we can determine board information. It is
118  * passed by the bootloader to the kernel.
119  */
120 extern cvmx_bootinfo_t *octeon_bootinfo;
121
122 /**
123  * Periodic timer to check auto negotiation
124  */
125 static struct callout cvm_oct_poll_timer;
126
127 /**
128  * Array of every ethernet device owned by this driver indexed by
129  * the ipd input port number.
130  */
131 struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
132
133 /**
134  * Task to handle link status changes.
135  */
136 static struct taskqueue *cvm_oct_link_taskq;
137
138 /**
139  * Function to update link status.
140  */
141 static void cvm_oct_update_link(void *context, int pending)
142 {
143         cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
144         struct ifnet *ifp = priv->ifp;
145         cvmx_helper_link_info_t link_info;
146
147         link_info.u64 = priv->link_info;
148
149         if (link_info.s.link_up) {
150                 if_link_state_change(ifp, LINK_STATE_UP);
151                 if (priv->queue != -1)
152                         DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
153                                    if_name(ifp), link_info.s.speed,
154                                    (link_info.s.full_duplex) ? "Full" : "Half",
155                                    priv->port, priv->queue);
156                 else
157                         DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
158                                    if_name(ifp), link_info.s.speed,
159                                    (link_info.s.full_duplex) ? "Full" : "Half",
160                                    priv->port);
161         } else {
162                 if_link_state_change(ifp, LINK_STATE_DOWN);
163                 DEBUGPRINT("%s: Link down\n", if_name(ifp));
164         }
165         priv->need_link_update = 0;
166 }
167
168 /**
169  * Periodic timer tick for slow management operations
170  *
171  * @param arg    Device to check
172  */
173 static void cvm_do_timer(void *arg)
174 {
175         static int port;
176         static int updated;
177         if (port < CVMX_PIP_NUM_INPUT_PORTS) {
178                 if (cvm_oct_device[port]) {
179                         int queues_per_port;
180                         int qos;
181                         cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
182                         if (priv->poll) 
183                         {
184                                 /* skip polling if we don't get the lock */
185                                 if (MDIO_TRYLOCK()) {
186                                         priv->poll(cvm_oct_device[port]);
187                                         MDIO_UNLOCK();
188
189                                         if (priv->need_link_update) {
190                                                 updated++;
191                                                 taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
192                                         }
193                                 }
194                         }
195
196                         queues_per_port = cvmx_pko_get_num_queues(port);
197                         /* Drain any pending packets in the free list */
198                         for (qos = 0; qos < queues_per_port; qos++) {
199                                 if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
200                                         IF_LOCK(&priv->tx_free_queue[qos]);
201                                         while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
202                                                 struct mbuf *m;
203
204                                                 _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
205                                                 m_freem(m);
206                                         }
207                                         IF_UNLOCK(&priv->tx_free_queue[qos]);
208
209                                         /*
210                                          * XXX locking!
211                                          */
212                                         priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
213                                 }
214                         }
215 #if 0
216                         cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
217 #endif
218                 }
219                 port++;
220                 /* Poll the next port in a 50th of a second.
221                    This spreads the polling of ports out a little bit */
222                 callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
223         } else {
224                 port = 0;
225                 /* If any updates were made in this run, continue iterating at
226                  * 1/50th of a second, so that if a link has merely gone down
227                  * temporarily (e.g. because of interface reinitialization) it
228                  * will not be forced to stay down for an entire second.
229                  */
230                 if (updated > 0) {
231                         updated = 0;
232                         callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
233                 } else {
234                         /* All ports have been polled. Start the next iteration through
235                            the ports in one second */
236                         callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
237                 }
238         }
239 }
240
241
242 /**
243  * Configure common hardware for all interfaces
244  */
245 static void cvm_oct_configure_common_hw(device_t bus)
246 {
247         struct octebus_softc *sc;
248         int error;
249         int rid;
250
251         sc = device_get_softc(bus);
252
253         /* Setup the FPA */
254         cvmx_fpa_enable();
255         cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
256         cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
257         if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
258                 cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
259
260         if (USE_RED)
261                 cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
262
263         /* Enable the MII interface */
264         if (!octeon_is_simulation())
265                 cvmx_write_csr(CVMX_SMI_EN, 1);
266
267         /* Register an IRQ hander for to receive POW interrupts */
268         rid = 0;
269         sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
270                                            CVMX_IRQ_WORKQ0 + pow_receive_group,
271                                            CVMX_IRQ_WORKQ0 + pow_receive_group,
272                                            1, RF_ACTIVE);
273         if (sc->sc_rx_irq == NULL) {
274                 device_printf(bus, "could not allocate workq irq");
275                 return;
276         }
277
278         error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
279                                cvm_oct_do_interrupt, NULL, cvm_oct_device,
280                                NULL);
281         if (error != 0) {
282                 device_printf(bus, "could not setup workq irq");
283                 return;
284         }
285
286
287 #ifdef SMP
288         if (USE_MULTICORE_RECEIVE) {
289                 critical_enter();
290                 {
291                         int cpu;
292                         for (cpu = 0; cpu < mp_maxid; cpu++) {
293                                 if (!CPU_ABSENT(cpu) &&
294                                    (cpu != PCPU_GET(cpuid))) {
295                                         cvmx_ciu_intx0_t en;
296                                         en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
297                                         en.s.workq |= (1<<pow_receive_group);
298                                         cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
299                                 }
300                         }
301                 }
302                 critical_exit();
303         }
304 #endif
305 }
306
307
308 /**
309  * Free a work queue entry received in a intercept callback.
310  *
311  * @param work_queue_entry
312  *               Work queue entry to free
313  * @return Zero on success, Negative on failure.
314  */
315 int cvm_oct_free_work(void *work_queue_entry)
316 {
317         cvmx_wqe_t *work = work_queue_entry;
318
319         int segments = work->word2.s.bufs;
320         cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
321
322         while (segments--) {
323                 cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
324                 if (__predict_false(!segment_ptr.s.i))
325                         cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
326                 segment_ptr = next_ptr;
327         }
328         cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
329
330         return 0;
331 }
332
333
334 /**
335  * Module/ driver initialization. Creates the linux network
336  * devices.
337  *
338  * @return Zero on success
339  */
340 int cvm_oct_init_module(device_t bus)
341 {
342         device_t dev;
343         int ifnum;
344         int num_interfaces;
345         int interface;
346         int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
347         int qos;
348
349         printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
350
351 #if 0
352         cvm_oct_proc_initialize();
353 #endif
354         cvm_oct_rx_initialize();
355         cvm_oct_configure_common_hw(bus);
356
357         cvmx_helper_initialize_packet_io_global();
358
359         /* Change the input group for all ports before input is enabled */
360         num_interfaces = cvmx_helper_get_number_of_interfaces();
361         for (interface = 0; interface < num_interfaces; interface++) {
362                 int num_ports = cvmx_helper_ports_on_interface(interface);
363                 int port;
364
365                 for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
366                         cvmx_pip_prt_tagx_t pip_prt_tagx;
367                         pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
368                         pip_prt_tagx.s.grp = pow_receive_group;
369                         cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
370                 }
371         }
372
373         cvmx_helper_ipd_and_packet_input_enable();
374
375         memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
376
377         cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
378             taskqueue_thread_enqueue, &cvm_oct_link_taskq);
379         taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
380             "octe link taskq");
381
382         /* Initialize the FAU used for counting packet buffers that need to be freed */
383         cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
384
385         if ((pow_send_group != -1)) {
386                 struct ifnet *ifp;
387
388                 printf("\tConfiguring device for POW only access\n");
389                 dev = BUS_ADD_CHILD(bus, 0, "pow", 0);
390                 if (dev != NULL)
391                         ifp = if_alloc(IFT_ETHER);
392                 if (dev != NULL && ifp != NULL) {
393                         /* Initialize the device private structure. */
394                         cvm_oct_private_t *priv;
395
396                         device_probe(dev);
397                         priv = device_get_softc(dev);
398                         priv->dev = dev;
399                         priv->ifp = ifp;
400                         priv->init = cvm_oct_common_init;
401                         priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
402                         priv->port = CVMX_PIP_NUM_INPUT_PORTS;
403                         priv->queue = -1;
404                         TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
405
406                         device_set_desc(dev, "Cavium Octeon POW Ethernet\n");
407
408                         ifp->if_softc = priv;
409
410                         if (priv->init(ifp) < 0) {
411                                 printf("\t\tFailed to register ethernet device for POW\n");
412                                 panic("%s: need to free ifp.", __func__);
413                         } else {
414                                 cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp;
415                                 printf("\t\t%s: POW send group %d, receive group %d\n",
416                                 if_name(ifp), pow_send_group, pow_receive_group);
417                         }
418                 } else {
419                         printf("\t\tFailed to allocate ethernet device for POW\n");
420                 }
421         }
422
423         ifnum = 0;
424         num_interfaces = cvmx_helper_get_number_of_interfaces();
425         for (interface = 0; interface < num_interfaces; interface++) {
426                 cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
427                 int num_ports = cvmx_helper_ports_on_interface(interface);
428                 int port;
429
430                 for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
431                         cvm_oct_private_t *priv;
432                         struct ifnet *ifp;
433                         
434                         dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
435                         if (dev != NULL)
436                                 ifp = if_alloc(IFT_ETHER);
437                         if (dev == NULL || ifp == NULL) {
438                                 printf("\t\tFailed to allocate ethernet device for port %d\n", port);
439                                 continue;
440                         }
441                         /* XXX/juli set max send q len.  */
442 #if 0
443                         if (disable_core_queueing)
444                                 ifp->tx_queue_len = 0;
445 #endif
446
447                         /* Initialize the device private structure. */
448                         device_probe(dev);
449                         priv = device_get_softc(dev);
450                         priv->dev = dev;
451                         priv->ifp = ifp;
452                         priv->imode = imode;
453                         priv->port = port;
454                         priv->queue = cvmx_pko_get_base_queue(priv->port);
455                         priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
456                         for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
457                                 cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
458                         TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
459
460                         switch (priv->imode) {
461
462                         /* These types don't support ports to IPD/PKO */
463                         case CVMX_HELPER_INTERFACE_MODE_DISABLED:
464                         case CVMX_HELPER_INTERFACE_MODE_PCIE:
465                         case CVMX_HELPER_INTERFACE_MODE_PICMG:
466                                 break;
467
468                         case CVMX_HELPER_INTERFACE_MODE_NPI:
469                                 priv->init = cvm_oct_common_init;
470                                 priv->uninit = cvm_oct_common_uninit;
471                                 device_set_desc(dev, "Cavium Octeon NPI Ethernet");
472                                 break;
473
474                         case CVMX_HELPER_INTERFACE_MODE_XAUI:
475                                 priv->init = cvm_oct_xaui_init;
476                                 priv->uninit = cvm_oct_xaui_uninit;
477                                 device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
478                                 break;
479
480                         case CVMX_HELPER_INTERFACE_MODE_LOOP:
481                                 priv->init = cvm_oct_common_init;
482                                 priv->uninit = cvm_oct_common_uninit;
483                                 device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
484                                 break;
485
486                         case CVMX_HELPER_INTERFACE_MODE_SGMII:
487                                 priv->init = cvm_oct_sgmii_init;
488                                 priv->uninit = cvm_oct_sgmii_uninit;
489                                 device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
490                                 break;
491
492                         case CVMX_HELPER_INTERFACE_MODE_SPI:
493                                 priv->init = cvm_oct_spi_init;
494                                 priv->uninit = cvm_oct_spi_uninit;
495                                 device_set_desc(dev, "Cavium Octeon SPI Ethernet");
496                                 break;
497
498                         case CVMX_HELPER_INTERFACE_MODE_RGMII:
499                                 priv->init = cvm_oct_rgmii_init;
500                                 priv->uninit = cvm_oct_rgmii_uninit;
501                                 device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
502                                 break;
503
504                         case CVMX_HELPER_INTERFACE_MODE_GMII:
505                                 priv->init = cvm_oct_rgmii_init;
506                                 priv->uninit = cvm_oct_rgmii_uninit;
507                                 device_set_desc(dev, "Cavium Octeon GMII Ethernet");
508                                 break;
509                         }
510
511                         ifp->if_softc = priv;
512
513                         if (!priv->init) {
514                                 panic("%s: unsupported device type, need to free ifp.", __func__);
515                         } else
516                         if (priv->init(ifp) < 0) {
517                                 printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
518                                 interface, priv->port);
519                                 panic("%s: init failed, need to free ifp.", __func__);
520                         } else {
521                                 cvm_oct_device[priv->port] = ifp;
522                                 fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
523                         }
524                 }
525         }
526
527         if (INTERRUPT_LIMIT) {
528                 /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
529                 cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
530
531                 /* Enable POW timer interrupt. It will count when there are packets available */
532                 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
533         } else {
534                 /* Enable POW interrupt when our port has at least one packet */
535                 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
536         }
537
538         callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
539         callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
540
541         return 0;
542 }
543
544
545 /**
546  * Module / driver shutdown
547  *
548  * @return Zero on success
549  */
550 void cvm_oct_cleanup_module(void)
551 {
552         int port;
553
554         /* Disable POW interrupt */
555         cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
556
557         cvmx_ipd_disable();
558
559 #if 0
560         /* Free the interrupt handler */
561         free_irq(8 + pow_receive_group, cvm_oct_device);
562 #endif
563
564         callout_stop(&cvm_oct_poll_timer);
565         cvm_oct_rx_shutdown();
566         cvmx_pko_disable();
567
568         /* Free the ethernet devices */
569         for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
570                 if (cvm_oct_device[port]) {
571                         cvm_oct_tx_shutdown(cvm_oct_device[port]);
572 #if 0
573                         unregister_netdev(cvm_oct_device[port]);
574                         kfree(cvm_oct_device[port]);
575 #else
576                         panic("%s: need to detach and free interface.", __func__);
577 #endif
578                         cvm_oct_device[port] = NULL;
579                 }
580         }
581
582         cvmx_pko_shutdown();
583 #if 0
584         cvm_oct_proc_shutdown();
585 #endif
586
587         cvmx_ipd_free_ptr();
588
589         /* Free the HW pools */
590         cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
591         cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
592         if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
593                 cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
594 }