]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/cavium/octe/ethernet.c
Merge Cavium Octeon SDK 2.0 Simple Executive; this brings some fixes and new
[FreeBSD/FreeBSD.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 extern int octeon_is_simulation(void);
78
79 /**
80  * Exported from the kernel so we can determine board information. It is
81  * passed by the bootloader to the kernel.
82  */
83 extern cvmx_bootinfo_t *octeon_bootinfo;
84
85 /**
86  * Periodic timer to check auto negotiation
87  */
88 static struct callout cvm_oct_poll_timer;
89
90 /**
91  * Array of every ethernet device owned by this driver indexed by
92  * the ipd input port number.
93  */
94 struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
95
96 /**
97  * Task to handle link status changes.
98  */
99 static struct taskqueue *cvm_oct_link_taskq;
100
101 /**
102  * Function to update link status.
103  */
104 static void cvm_oct_update_link(void *context, int pending)
105 {
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;
109
110         link_info.u64 = priv->link_info;
111
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);
118         } else {
119                 if_link_state_change(ifp, LINK_STATE_DOWN);
120                 DEBUGPRINT("%s: Link down\n", if_name(ifp));
121         }
122         priv->need_link_update = 0;
123 }
124
125 /**
126  * Periodic timer tick for slow management operations
127  *
128  * @param arg    Device to check
129  */
130 static void cvm_do_timer(void *arg)
131 {
132         static int port;
133         static int updated;
134         if (port < CVMX_PIP_NUM_INPUT_PORTS) {
135                 if (cvm_oct_device[port]) {
136                         int queues_per_port;
137                         int qos;
138                         cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
139                         if (priv->poll) 
140                         {
141                                 /* skip polling if we don't get the lock */
142                                 if (MDIO_TRYLOCK()) {
143                                         priv->poll(cvm_oct_device[port]);
144                                         MDIO_UNLOCK();
145
146                                         if (priv->need_link_update) {
147                                                 updated++;
148                                                 taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
149                                         }
150                                 }
151                         }
152
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)) {
159                                                 struct mbuf *m;
160
161                                                 _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
162                                                 m_freem(m);
163                                         }
164                                         IF_UNLOCK(&priv->tx_free_queue[qos]);
165
166                                         /*
167                                          * XXX locking!
168                                          */
169                                         priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
170                                 }
171                         }
172                 }
173                 port++;
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);
177         } else {
178                 port = 0;
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.
183                  */
184                 if (updated > 0) {
185                         updated = 0;
186                         callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
187                 } else {
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);
191                 }
192         }
193 }
194
195
196 /**
197  * Configure common hardware for all interfaces
198  */
199 static void cvm_oct_configure_common_hw(device_t bus)
200 {
201         struct octebus_softc *sc;
202         int error;
203         int rid;
204
205         sc = device_get_softc(bus);
206
207         /* Setup the FPA */
208         cvmx_fpa_enable();
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);
213
214         if (USE_RED)
215                 cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
216
217         /* Enable the MII interface */
218         if (!octeon_is_simulation())
219                 cvmx_write_csr(CVMX_SMI_EN, 1);
220
221         /* Register an IRQ hander for to receive POW interrupts */
222         rid = 0;
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,
226                                            1, RF_ACTIVE);
227         if (sc->sc_rx_irq == NULL) {
228                 device_printf(bus, "could not allocate workq irq");
229                 return;
230         }
231
232         error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
233                                cvm_oct_do_interrupt, NULL, cvm_oct_device,
234                                NULL);
235         if (error != 0) {
236                 device_printf(bus, "could not setup workq irq");
237                 return;
238         }
239
240
241 #ifdef SMP
242         if (USE_MULTICORE_RECEIVE) {
243                 critical_enter();
244                 {
245                         int cpu;
246                         for (cpu = 0; cpu < mp_maxid; cpu++) {
247                                 if (!CPU_ABSENT(cpu) &&
248                                    (cpu != PCPU_GET(cpuid))) {
249                                         cvmx_ciu_intx0_t en;
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);
253                                 }
254                         }
255                 }
256                 critical_exit();
257         }
258 #endif
259 }
260
261
262 /**
263  * Free a work queue entry received in a intercept callback.
264  *
265  * @param work_queue_entry
266  *               Work queue entry to free
267  * @return Zero on success, Negative on failure.
268  */
269 int cvm_oct_free_work(void *work_queue_entry)
270 {
271         cvmx_wqe_t *work = work_queue_entry;
272
273         int segments = work->word2.s.bufs;
274         cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
275
276         while (segments--) {
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;
281         }
282         cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
283
284         return 0;
285 }
286
287
288 /**
289  * Module/ driver initialization. Creates the linux network
290  * devices.
291  *
292  * @return Zero on success
293  */
294 int cvm_oct_init_module(device_t bus)
295 {
296         device_t dev;
297         int ifnum;
298         int num_interfaces;
299         int interface;
300         int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
301         int qos;
302
303         printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
304
305         cvm_oct_rx_initialize();
306         cvm_oct_configure_common_hw(bus);
307
308         cvmx_helper_initialize_packet_io_global();
309
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);
314                 int port;
315
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);
321                 }
322         }
323
324         cvmx_helper_ipd_and_packet_input_enable();
325
326         memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
327
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,
331             "octe link taskq");
332
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);
335
336         ifnum = 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);
341                 int port;
342
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;
345                         struct ifnet *ifp;
346                         
347                         dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
348                         if (dev != NULL)
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);
352                                 continue;
353                         }
354
355                         /* Initialize the device private structure. */
356                         device_probe(dev);
357                         priv = device_get_softc(dev);
358                         priv->dev = dev;
359                         priv->ifp = ifp;
360                         priv->imode = imode;
361                         priv->port = port;
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);
367
368                         switch (priv->imode) {
369
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:
374                                 break;
375
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");
380                                 break;
381
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");
386                                 break;
387
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");
392                                 break;
393
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");
398                                 break;
399
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");
404                                 break;
405
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");
410                                 break;
411
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");
416                                 break;
417                         }
418
419                         ifp->if_softc = priv;
420
421                         if (!priv->init) {
422                                 panic("%s: unsupported device type, need to free ifp.", __func__);
423                         } else
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__);
428                         } else {
429                                 cvm_oct_device[priv->port] = ifp;
430                                 fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
431                         }
432                 }
433         }
434
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);
438
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);
441         } else {
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);
444         }
445
446         callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
447         callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
448
449         return 0;
450 }
451
452
453 /**
454  * Module / driver shutdown
455  *
456  * @return Zero on success
457  */
458 void cvm_oct_cleanup_module(void)
459 {
460         int port;
461
462         /* Disable POW interrupt */
463         cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
464
465 #if 0
466         /* Free the interrupt handler */
467         free_irq(8 + pow_receive_group, cvm_oct_device);
468 #endif
469
470         callout_stop(&cvm_oct_poll_timer);
471         cvm_oct_rx_shutdown();
472
473         cvmx_helper_shutdown_packet_io_global();
474
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]);
479 #if 0
480                         unregister_netdev(cvm_oct_device[port]);
481                         kfree(cvm_oct_device[port]);
482 #else
483                         panic("%s: need to detach and free interface.", __func__);
484 #endif
485                         cvm_oct_device[port] = NULL;
486                 }
487         }
488 }