usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src-rt / et / sys / et_cfe.c
blobe74e7d03795e4af9d374b25c0e44d98e29fbd5a9
1 /*
2 * CFE polled-mode device driver for
3 * Broadcom BCM47XX 10/100 Mbps Ethernet Controller
5 * Copyright (C) 2010, Broadcom Corporation
6 * All Rights Reserved.
7 *
8 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
9 * the contents of this file may not be disclosed to third parties, copied
10 * or duplicated in any form, in whole or in part, without the prior
11 * written permission of Broadcom Corporation.
13 * $Id: et_cfe.c,v 1.25 2009-10-18 18:35:22 Exp $
16 #include "lib_types.h"
17 #include "lib_malloc.h"
18 #include "lib_string.h"
19 #include "lib_printf.h"
21 #include "cfe_iocb.h"
22 #include "cfe_device.h"
23 #include "cfe_ioctl.h"
24 #include "cfe_console.h"
25 #include "cfe_timer.h"
26 #include "cfe_error.h"
27 #include "ui_command.h"
29 #include <typedefs.h>
30 #include <osl.h>
31 #include <epivers.h>
32 #include <bcmendian.h>
33 #include <proto/ethernet.h>
34 #include <bcmdevs.h>
35 #include <bcmenetmib.h>
36 #include <bcmenetrxh.h>
37 #include <bcmutils.h>
38 #include <et_dbg.h>
39 #include <hndsoc.h>
40 #include <bcmgmacrxh.h>
41 #include <etc.h>
43 typedef struct et_info {
44 etc_info_t *etc; /* pointer to common os-independent data */
45 cfe_devctx_t *ctx; /* backpoint to device */
46 int64_t timer; /* one second watchdog timer */
47 osl_t *osh;
48 struct et_info *next; /* pointer to next et_info_t in chain */
49 } et_info_t;
51 static et_info_t *et_list = NULL;
53 void et_init(et_info_t *et, uint options);
54 void et_reset(et_info_t *et);
55 void et_link_up(et_info_t *et);
56 void et_link_down(et_info_t *et);
57 int et_up(et_info_t *et);
58 int et_down(et_info_t *et, int reset);
59 void et_dump(et_info_t *et, struct bcmstrbuf *b);
60 void et_addcmd(void);
61 void et_intrson(et_info_t *et);
63 void
64 et_init(et_info_t *et, uint options)
66 ET_TRACE(("et%d: et_init\n", et->etc->unit));
68 etc_reset(et->etc);
69 etc_init(et->etc, options);
72 void
73 et_reset(et_info_t *et)
75 ET_TRACE(("et%d: et_reset\n", et->etc->unit));
77 etc_reset(et->etc);
80 void
81 et_link_up(et_info_t *et)
83 ET_ERROR(("et%d: link up (%d%s)\n",
84 et->etc->unit, et->etc->speed, (et->etc->duplex? "FD" : "HD")));
87 void
88 et_link_down(et_info_t *et)
90 ET_ERROR(("et%d: link down\n", et->etc->unit));
93 int
94 et_up(et_info_t *et)
96 if (et->etc->up)
97 return 0;
99 ET_TRACE(("et%d: et_up\n", et->etc->unit));
101 etc_up(et->etc);
103 /* schedule one second watchdog timer */
104 TIMER_SET(et->timer, CFE_HZ / 2);
106 return 0;
110 et_down(et_info_t *et, int reset)
112 ET_TRACE(("et%d: et_down\n", et->etc->unit));
114 /* stop watchdog timer */
115 TIMER_CLEAR(et->timer);
117 etc_down(et->etc, reset);
119 return 0;
122 void
123 et_dump(et_info_t *et, struct bcmstrbuf *b)
125 #ifdef BCMDBG
126 etc_dump(et->etc, b);
127 #endif
130 et_info_t *et_phyfind(et_info_t *et, uint coreunit);
131 uint16 et_phyrd(et_info_t *et, uint phyaddr, uint reg);
132 void et_phywr(et_info_t *et, uint reg, uint phyaddr, uint16 val);
135 * 47XX-specific shared mdc/mdio contortion:
136 * Find the et associated with the same chip as <et>
137 * and coreunit matching <coreunit>.
139 et_info_t *
140 et_phyfind(et_info_t *et, uint coreunit)
142 et_info_t *tmp;
144 /* walk the list et's */
145 for (tmp = et_list; tmp; tmp = tmp->next) {
146 if (et->etc == NULL)
147 continue;
148 if (tmp->etc->coreunit != coreunit)
149 continue;
150 break;
152 return (tmp);
155 /* shared phy read entry point */
156 uint16
157 et_phyrd(et_info_t *et, uint phyaddr, uint reg)
159 return et->etc->chops->phyrd(et->etc->ch, phyaddr, reg);
162 /* shared phy write entry point */
163 void
164 et_phywr(et_info_t *et, uint phyaddr, uint reg, uint16 val)
166 et->etc->chops->phywr(et->etc->ch, phyaddr, reg, val);
169 /* *********************************************************************
170 * ETHER_PROBE(drv,probe_a,probe_b,probe_ptr)
172 * Probe and install driver.
173 * Create a context structure and attach to the
174 * specified network device.
176 * Input parameters:
177 * drv - driver descriptor
178 * probe_a - device ID
179 * probe_b - unit number
180 * probe_ptr - mapped registers
182 * Return value:
183 * nothing
184 ********************************************************************* */
186 static void
187 et_probe(cfe_driver_t *drv,
188 unsigned long probe_a, unsigned long probe_b,
189 void *probe_ptr)
191 et_info_t *et;
192 uint16 device;
193 uint unit;
194 char name[128];
196 device = (uint16) probe_a;
197 unit = (uint) probe_b;
199 if (!(et = (et_info_t *) KMALLOC(sizeof(et_info_t), 0))) {
200 ET_ERROR(("et%d: KMALLOC failed\n", unit));
201 return;
203 bzero(et, sizeof(et_info_t));
205 et->osh = osl_attach(et);
206 ASSERT(et->osh);
208 #ifdef CFG_SIM
209 /* Make it chatty in simulation */
210 et_msg_level = 0xf;
211 #endif
213 /* common load-time initialization */
214 if ((et->etc = etc_attach(et, VENDOR_BROADCOM, device, unit, et->osh, probe_ptr)) == NULL) {
215 ET_ERROR(("et%d: etc_attach failed\n", unit));
216 KFREE(et);
217 return;
220 /* this is a polling driver - the chip intmask stays zero */
221 et->etc->chops->intrsoff(et->etc->ch);
223 /* add us to the global linked list */
224 et->next = et_list;
225 et_list = et;
227 /* print hello string */
228 et->etc->chops->longname(et->etc->ch, name, sizeof (name));
229 printf("et%d: %s %s\n", unit, name, EPI_VERSION_STR);
231 cfe_attach(drv, et, NULL, name);
234 /* *********************************************************************
235 * ETHER_OPEN(ctx)
237 * Open the Ethernet device. The MAC is reset, initialized, and
238 * prepared to receive and send packets.
240 * Input parameters:
241 * ctx - device context (includes ptr to our softc)
243 * Return value:
244 * status, 0 = ok
245 ********************************************************************* */
247 static int
248 et_open(cfe_devctx_t *ctx)
250 et_info_t *et = (et_info_t *) ctx->dev_softc;
252 ET_TRACE(("et%d: et_open\n", et->etc->unit));
254 return et_up(et);
257 /* *********************************************************************
258 * ETHER_READ(ctx,buffer)
260 * Read a packet from the Ethernet device. If no packets are
261 * available, the read will succeed but return 0 bytes.
263 * Input parameters:
264 * ctx - device context (includes ptr to our softc)
265 * buffer - pointer to buffer descriptor.
267 * Return value:
268 * status, 0 = ok
269 ********************************************************************* */
271 static int
272 et_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
274 et_info_t *et = (et_info_t *) ctx->dev_softc;
275 int events;
276 void *p;
277 bcmenetrxh_t *rxh;
278 uint16 flags;
279 char eabuf[32];
281 ET_TRACE(("et%d: et_read\n", et->etc->unit));
283 /* assume no packets */
284 buffer->buf_retlen = 0;
286 /* poll for packet */
287 events = et->etc->chops->getintrevents(et->etc->ch, FALSE);
288 if (events & INTR_ERROR)
289 return CFE_ERR_IOERR;
290 if ((events & INTR_RX) == 0)
291 return 0;
293 /* get packet */
294 if (!(p = et->etc->chops->rx(et->etc->ch)))
295 goto done;
297 /* packet buffer starts with rxhdr */
298 rxh = (bcmenetrxh_t *) PKTDATA(NULL, p);
300 /* strip off rxhdr */
301 PKTPULL(NULL, p, HWRXOFF);
303 /* check for reported frame errors */
304 flags = RXH_FLAGS(et->etc, rxh);
305 if (flags) {
306 bcm_ether_ntoa((struct ether_addr *)
307 (((struct ether_header *) PKTDATA(NULL, p))->ether_shost), eabuf);
308 if (RXH_OVERSIZE(et->etc, rxh)) {
309 ET_ERROR(("et%d: rx: over size packet from %s\n", et->etc->unit, eabuf));
311 if (RXH_CRC(et->etc, rxh)) {
312 ET_ERROR(("et%d: rx: crc error from %s\n", et->etc->unit, eabuf));
314 if (RXH_OVF(et->etc, rxh)) {
315 ET_ERROR(("et%d: rx: fifo overflow\n", et->etc->unit));
317 if (RXH_NO(et->etc, rxh)) {
318 ET_ERROR(("et%d: rx: crc error (odd nibbles) from %s\n",
319 et->etc->unit, eabuf));
321 if (RXH_RXER(et->etc, rxh)) {
322 ET_ERROR(("et%d: rx: symbol error from %s\n", et->etc->unit, eabuf));
324 } else {
325 bcopy(PKTDATA(NULL, p), buffer->buf_ptr, PKTLEN(NULL, p));
326 buffer->buf_retlen = PKTLEN(NULL, p);
327 ET_PRHDR("rx", (struct ether_header *) buffer->buf_ptr,
328 buffer->buf_retlen, et->etc->unit);
329 ET_PRPKT("rxpkt", buffer->buf_ptr, buffer->buf_retlen, et->etc->unit);
332 /* free packet */
333 PKTFREE(et->osh, p, FALSE);
335 done:
336 /* post more rx bufs */
337 et->etc->chops->rxfill(et->etc->ch);
339 return 0;
342 /* *********************************************************************
343 * ETHER_INPSTAT(ctx,inpstat)
345 * Check for received packets on the Ethernet device
347 * Input parameters:
348 * ctx - device context (includes ptr to our softc)
349 * inpstat - pointer to input status structure
351 * Return value:
352 * status, 0 = ok
353 ********************************************************************* */
355 static int
356 et_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
358 et_info_t *et = (et_info_t *) ctx->dev_softc;
359 int events;
361 events = et->etc->chops->getintrevents(et->etc->ch, FALSE);
362 inpstat->inp_status = ((events & INTR_RX) ? 1 : 0);
364 return 0;
367 /* *********************************************************************
368 * ETHER_WRITE(ctx,buffer)
370 * Write a packet to the Ethernet device.
372 * Input parameters:
373 * ctx - device context (includes ptr to our softc)
374 * buffer - pointer to buffer descriptor.
376 * Return value:
377 * status, 0 = ok
378 ********************************************************************* */
380 static int
381 et_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
383 et_info_t *et = ctx->dev_softc;
384 int events;
385 void *p;
387 if (!(p = PKTGET(NULL, buffer->buf_length, TRUE))) {
388 ET_ERROR(("et%d: PKTGET failed\n", et->etc->unit));
389 return CFE_ERR_NOMEM;
392 bcopy(buffer->buf_ptr, PKTDATA(NULL, p), buffer->buf_length);
394 ET_PRHDR("tx", (struct ether_header *)PKTDATA(NULL, p), PKTLEN(NULL, p), et->etc->unit);
395 ET_PRPKT("txpkt", PKTDATA(NULL, p), PKTLEN(NULL, p), et->etc->unit);
397 ASSERT(*et->etc->txavail[TX_Q0] > 0);
399 /* transmit the frame */
400 et->etc->chops->tx(et->etc->ch, p);
402 while (((events = et->etc->chops->getintrevents(et->etc->ch, FALSE)) & (INTR_ERROR | INTR_TX)) == 0);
404 /* reclaim any completed tx frames */
405 et->etc->chops->txreclaim(et->etc->ch, FALSE);
407 return (events & INTR_ERROR) ? CFE_ERR_IOERR : CFE_OK;
410 /* *********************************************************************
411 * ETHER_IOCTL(ctx,buffer)
413 * Do device-specific I/O control operations for the device
415 * Input parameters:
416 * ctx - device context (includes ptr to our softc)
417 * buffer - pointer to buffer descriptor.
419 * Return value:
420 * status, 0 = ok
421 ********************************************************************* */
422 static int
423 et_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
425 et_info_t *et = (et_info_t *) ctx->dev_softc;
426 int val;
428 ET_TRACE(("et%d: et_ioctl: cmd 0x%x\n", et->etc->unit, buffer->buf_ioctlcmd));
430 switch (buffer->buf_ioctlcmd) {
432 case IOCTL_ETHER_GETHWADDR:
433 bcopy(&et->etc->cur_etheraddr, buffer->buf_ptr, ETHER_ADDR_LEN);
434 break;
436 case IOCTL_ETHER_SETHWADDR:
437 bcopy(buffer->buf_ptr, &et->etc->cur_etheraddr, ETHER_ADDR_LEN);
438 et_init(et, ET_INIT_DEF_OPTIONS);
439 break;
441 case IOCTL_ETHER_GETSPEED:
442 val = ETHER_SPEED_UNKNOWN;
443 if (et->etc->linkstate) {
444 if (et->etc->speed == 10)
445 val = et->etc->duplex ? ETHER_SPEED_10FDX : ETHER_SPEED_10HDX;
446 else if (et->etc->speed == 100)
447 val = et->etc->duplex ? ETHER_SPEED_100FDX : ETHER_SPEED_100HDX;
448 else if (et->etc->speed == 1000)
449 val = ETHER_SPEED_1000FDX;
451 *((int *) buffer->buf_ptr) = val;
452 break;
454 case IOCTL_ETHER_SETSPEED:
455 val = *((int *) buffer->buf_ptr);
456 if (val == ETHER_SPEED_AUTO)
457 val = ET_AUTO;
458 else if (val == ETHER_SPEED_10HDX)
459 val = ET_10HALF;
460 else if (val == ETHER_SPEED_10FDX)
461 val = ET_10FULL;
462 else if (val == ETHER_SPEED_100HDX)
463 val = ET_100HALF;
464 else if (val == ETHER_SPEED_100FDX)
465 val = ET_100FULL;
466 else if (val == ETHER_SPEED_1000FDX)
467 val = ET_1000FULL;
468 else
469 return CFE_ERR_UNSUPPORTED;
470 return etc_ioctl(et->etc, ETCSPEED, &val);
472 case IOCTL_ETHER_GETLINK:
473 *((int *) buffer->buf_ptr) = (int) et->etc->linkstate;
474 break;
476 case IOCTL_ETHER_GETLOOPBACK:
477 *((int *) buffer->buf_ptr) = et->etc->loopbk;
478 break;
480 case IOCTL_ETHER_SETLOOPBACK:
481 val = *((int *) buffer->buf_ptr);
482 if (val == ETHER_LOOPBACK_OFF)
483 val = (int) FALSE;
484 else
485 val = (int) TRUE;
486 return etc_ioctl(et->etc, ETCLOOP, &val);
488 default:
489 return CFE_ERR_UNSUPPORTED;
493 return 0;
496 /* *********************************************************************
497 * ETHER_CLOSE(ctx)
499 * Close the Ethernet device.
501 * Input parameters:
502 * ctx - device context (includes ptr to our softc)
504 * Return value:
505 * status, 0 = ok
506 ********************************************************************* */
508 static int
509 et_close(cfe_devctx_t *ctx)
511 et_info_t *et = (et_info_t *) ctx->dev_softc;
513 ET_TRACE(("et%d: et_close\n", et->etc->unit));
515 return et_down(et, 1);
518 void
519 et_intrson(et_info_t *et)
521 /* this is a polling driver - the chip intmask stays zero */
522 ET_TRACE(("et%d: et_intrson\n", et->etc->unit));
525 /* *********************************************************************
526 * ETHER_POLL(ctx,ticks)
528 * Check for changes in the PHY, so we can track speed changes.
530 * Input parameters:
531 * ctx - device context (includes ptr to our softc)
532 * ticks- current time in ticks
534 * Return value:
535 * nothing
536 ********************************************************************* */
538 static void
539 et_poll(cfe_devctx_t *ctx, int64_t ticks)
541 et_info_t *et = (et_info_t *) ctx->dev_softc;
543 if (TIMER_RUNNING(et->timer) &&
544 TIMER_EXPIRED(et->timer)) {
545 etc_watchdog(et->etc);
546 TIMER_SET(et->timer, CFE_HZ / 2);
550 const static cfe_devdisp_t et_dispatch = {
551 et_open,
552 et_read,
553 et_inpstat,
554 et_write,
555 et_ioctl,
556 et_close,
557 et_poll,
558 NULL
561 const cfe_driver_t bcmet = {
562 "Broadcom Ethernet",
563 "eth",
564 CFE_DEV_NETWORK,
565 &et_dispatch,
566 et_probe
569 static int
570 ui_cmd_et(ui_cmdline_t *cmdline, int argc, char *argv[])
572 char *command, *name;
573 et_info_t *et;
574 cfe_device_t *dev;
575 int cmd, val, ret;
576 char *arg = (char *) &val;
578 if (!(command = cmd_getarg(cmdline, 0)))
579 return CFE_ERR_INV_PARAM;
581 if (!cmd_sw_value(cmdline, "-i", &name) || !name)
582 name = "eth0";
583 if (!(dev = cfe_finddev(name)) ||
584 !dev->dev_softc)
585 return CFE_ERR_DEVNOTFOUND;
586 for (et = et_list; et; et = et->next)
587 if (et == dev->dev_softc)
588 break;
589 if (!et && !(et = et_list))
590 return CFE_ERR_DEVNOTFOUND;
592 if (!strcmp(command, "up"))
593 cmd = ETCUP;
594 else if (!strcmp(command, "down"))
595 cmd = ETCDOWN;
596 else if (!strcmp(command, "loop")) {
597 cmd = ETCLOOP;
598 arg = cmd_getarg(cmdline, 1);
600 else if (!strcmp(command, "dump")) {
601 char *p;
602 if (!(arg = KMALLOC(4096, 0)))
603 return CFE_ERR_NOMEM;
604 bzero(arg, 4096);
605 if ((ret = etc_ioctl(et->etc, ETCDUMP, arg))) {
606 KFREE(arg);
607 return ret;
609 /* No puts in cfe, and printf only has a 512 byte buffer */
610 p = arg;
611 while (*p)
612 printf("%c", *p);
613 KFREE(arg);
614 return 0;
616 else if (!strcmp(command, "msglevel")) {
617 cmd = ETCSETMSGLEVEL;
618 arg = cmd_getarg(cmdline, 1);
620 else if (!strcmp(command, "promisc")) {
621 cmd = ETCPROMISC;
622 arg = cmd_getarg(cmdline, 1);
624 else
625 return CFE_ERR_INV_PARAM;
627 if (!arg)
628 return CFE_ERR_INV_PARAM;
629 else if (arg != (char *) &val) {
630 val = bcm_strtoul(arg, NULL, 0);
631 arg = (char *) &val;
634 return etc_ioctl(et->etc, cmd, arg);
637 void
638 et_addcmd(void)
640 cmd_addcmd("et",
641 ui_cmd_et,
642 NULL,
643 "Broadcom Ethernet utility.",
644 "et command [args..]\n\n"
645 "Configures the specified Broadcom Ethernet interface.",
646 "-i=*;Specifies the interface|"
647 "up;Activate the specified interface|"
648 "down;Deactivate the specified interface|"
649 "loop;Sets the loopback mode (0,1)|"
650 "dump;Dump driver information|"
651 "msglevel;Sets the driver message level|"
652 "promisc;Sets promiscuous mode|");