1/* 2 * CFE polled-mode device driver for 3 * Broadcom BCM47XX 10/100 Mbps Ethernet Controller 4 * 5 * Copyright 2007, 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. 12 * 13 * $Id: et_cfe.c,v 1.1.1.1 2008/10/15 03:25:54 james26_jang Exp $ 14 */ 15 16#include "lib_types.h" 17#include "lib_malloc.h" 18#include "lib_string.h" 19#include "lib_printf.h" 20 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" 28 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 <etc.h> 40 41typedef struct et_info { 42 etc_info_t *etc; /* pointer to common os-independent data */ 43 cfe_devctx_t *ctx; /* backpoint to device */ 44 int64_t timer; /* one second watchdog timer */ 45 osl_t *osh; 46 struct et_info *next; /* pointer to next et_info_t in chain */ 47} et_info_t; 48 49static et_info_t *et_list = NULL; 50 51void et_init(et_info_t *et); 52void et_reset(et_info_t *et); 53void et_link_up(et_info_t *et); 54void et_link_down(et_info_t *et); 55int et_up(et_info_t *et); 56int et_down(et_info_t *et, int reset); 57void et_dump(et_info_t *et, struct bcmstrbuf *b); 58void et_addcmd(void); 59void et_intrson(et_info_t *et); 60 61void 62et_init(et_info_t *et) 63{ 64 ET_TRACE(("et%d: et_init\n", et->etc->unit)); 65 66 etc_reset(et->etc); 67 etc_init(et->etc); 68} 69 70void 71et_reset(et_info_t *et) 72{ 73 ET_TRACE(("et%d: et_reset\n", et->etc->unit)); 74 75 etc_reset(et->etc); 76} 77 78void 79et_link_up(et_info_t *et) 80{ 81 ET_ERROR(("et%d: link up (%d%s)\n", 82 et->etc->unit, et->etc->speed, (et->etc->duplex? "FD" : "HD"))); 83} 84 85void 86et_link_down(et_info_t *et) 87{ 88 ET_ERROR(("et%d: link down\n", et->etc->unit)); 89} 90 91int 92et_up(et_info_t *et) 93{ 94 if (et->etc->up) 95 return 0; 96 97 ET_TRACE(("et%d: et_up\n", et->etc->unit)); 98 99 etc_up(et->etc); 100 101 /* schedule one second watchdog timer */ 102 TIMER_SET(et->timer, CFE_HZ / 2); 103 104 return 0; 105} 106 107int 108et_down(et_info_t *et, int reset) 109{ 110 ET_TRACE(("et%d: et_down\n", et->etc->unit)); 111 112 /* stop watchdog timer */ 113 TIMER_CLEAR(et->timer); 114 115 etc_down(et->etc, reset); 116 117 return 0; 118} 119 120void 121et_dump(et_info_t *et, struct bcmstrbuf *b) 122{ 123} 124 125 126et_info_t *et_phyfind(et_info_t *et, uint coreunit); 127uint16 et_phyrd(et_info_t *et, uint phyaddr, uint reg); 128void et_phywr(et_info_t *et, uint reg, uint phyaddr, uint16 val); 129 130/* 131 * 47XX-specific shared mdc/mdio contortion: 132 * Find the et associated with the same chip as <et> 133 * and coreunit matching <coreunit>. 134 */ 135et_info_t * 136et_phyfind(et_info_t *et, uint coreunit) 137{ 138 et_info_t *tmp; 139 140 /* walk the list et's */ 141 for (tmp = et_list; tmp; tmp = tmp->next) { 142 if (et->etc == NULL) 143 continue; 144 if (tmp->etc->coreunit != coreunit) 145 continue; 146 break; 147 } 148 return (tmp); 149} 150 151/* shared phy read entry point */ 152uint16 153et_phyrd(et_info_t *et, uint phyaddr, uint reg) 154{ 155 return et->etc->chops->phyrd(et->etc->ch, phyaddr, reg); 156} 157 158/* shared phy write entry point */ 159void 160et_phywr(et_info_t *et, uint phyaddr, uint reg, uint16 val) 161{ 162 et->etc->chops->phywr(et->etc->ch, phyaddr, reg, val); 163} 164 165 166/* ********************************************************************* 167 * ETHER_PROBE(drv,probe_a,probe_b,probe_ptr) 168 * 169 * Probe and install driver. 170 * Create a context structure and attach to the 171 * specified network device. 172 * 173 * Input parameters: 174 * drv - driver descriptor 175 * probe_a - device ID 176 * probe_b - unit number 177 * probe_ptr - mapped registers 178 * 179 * Return value: 180 * nothing 181 ********************************************************************* */ 182 183static void 184et_probe(cfe_driver_t *drv, 185 unsigned long probe_a, unsigned long probe_b, 186 void *probe_ptr) 187{ 188 et_info_t *et; 189 uint16 device; 190 uint unit; 191 char name[128]; 192 193 device = (uint16) probe_a; 194 unit = (uint) probe_b; 195 196 if (!(et = (et_info_t *) KMALLOC(sizeof(et_info_t), 0))) { 197 ET_ERROR(("et%d: KMALLOC failed\n", unit)); 198 return; 199 } 200 bzero(et, sizeof(et_info_t)); 201 202 et->osh = osl_attach(et); 203 ASSERT(et->osh); 204 205 /* common load-time initialization */ 206 if ((et->etc = etc_attach(et, VENDOR_BROADCOM, device, unit, et->osh, probe_ptr)) == NULL) { 207 ET_ERROR(("et%d: etc_attach failed\n", unit)); 208 KFREE(et); 209 return; 210 } 211 212 /* this is a polling driver - the chip intmask stays zero */ 213 et->etc->chops->intrsoff(et->etc->ch); 214 215 /* add us to the global linked list */ 216 et->next = et_list; 217 et_list = et; 218 219 /* print hello string */ 220 et->etc->chops->longname(et->etc->ch, name, sizeof (name)); 221 printf("et%d: %s %s\n", unit, name, EPI_VERSION_STR); 222 223 cfe_attach(drv, et, NULL, name); 224} 225 226/* ********************************************************************* 227 * ETHER_OPEN(ctx) 228 * 229 * Open the Ethernet device. The MAC is reset, initialized, and 230 * prepared to receive and send packets. 231 * 232 * Input parameters: 233 * ctx - device context (includes ptr to our softc) 234 * 235 * Return value: 236 * status, 0 = ok 237 ********************************************************************* */ 238 239static int 240et_open(cfe_devctx_t *ctx) 241{ 242 et_info_t *et = (et_info_t *) ctx->dev_softc; 243 244 ET_TRACE(("et%d: et_open\n", et->etc->unit)); 245 246 return et_up(et); 247} 248 249/* ********************************************************************* 250 * ETHER_READ(ctx,buffer) 251 * 252 * Read a packet from the Ethernet device. If no packets are 253 * available, the read will succeed but return 0 bytes. 254 * 255 * Input parameters: 256 * ctx - device context (includes ptr to our softc) 257 * buffer - pointer to buffer descriptor. 258 * 259 * Return value: 260 * status, 0 = ok 261 ********************************************************************* */ 262 263static int 264et_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) 265{ 266 et_info_t *et = (et_info_t *) ctx->dev_softc; 267 int events; 268 void *p; 269 bcmenetrxh_t *rxh; 270 uint16 flags; 271 char eabuf[32]; 272 273 ET_TRACE(("et%d: et_read\n", et->etc->unit)); 274 275 /* assume no packets */ 276 buffer->buf_retlen = 0; 277 278 /* poll for packet */ 279 events = et->etc->chops->getintrevents(et->etc->ch, FALSE); 280 if ((events & INTR_RX) == 0) 281 return 0; 282 283 /* get packet */ 284 if (!(p = et->etc->chops->rx(et->etc->ch))) 285 goto done; 286 287 /* packet buffer starts with rxhdr */ 288 rxh = (bcmenetrxh_t *) PKTDATA(NULL, p); 289 290 /* strip off rxhdr */ 291 PKTPULL(NULL, p, HWRXOFF); 292 293 /* check for reported frame errors */ 294 flags = ltoh16(rxh->flags); 295 if ((flags & (RXF_NO | RXF_RXER | RXF_CRC | RXF_OV))) { 296 bcm_ether_ntoa((struct ether_addr *) 297 (((struct ether_header *) PKTDATA(NULL, p))->ether_shost), eabuf); 298 if (flags & RXF_NO) { 299 ET_ERROR(("et%d: rx: crc error (odd nibbles) from %s\n", et->etc->unit, eabuf)); 300 } 301 if (flags & RXF_RXER) { 302 ET_ERROR(("et%d: rx: symbol error from %s\n", et->etc->unit, eabuf)); 303 } 304 if (flags & RXF_CRC) { 305 ET_ERROR(("et%d: rx: crc error from %s\n", et->etc->unit, eabuf)); 306 } 307 if (flags & RXF_OV) { 308 ET_ERROR(("et%d: rx: fifo overflow\n", et->etc->unit)); 309 } 310 } else { 311 bcopy(PKTDATA(NULL, p), buffer->buf_ptr, PKTLEN(NULL, p)); 312 buffer->buf_retlen = PKTLEN(NULL, p); 313 ET_PRHDR("rx", (struct ether_header *) buffer->buf_ptr, buffer->buf_retlen, et->etc->unit); 314 ET_PRPKT("rxpkt", buffer->buf_ptr, buffer->buf_retlen, et->etc->unit); 315 } 316 317 /* free packet */ 318 PKTFREE(et->osh, p, FALSE); 319 320 done: 321 /* post more rx bufs */ 322 et->etc->chops->rxfill(et->etc->ch); 323 324 return 0; 325} 326 327/* ********************************************************************* 328 * ETHER_INPSTAT(ctx,inpstat) 329 * 330 * Check for received packets on the Ethernet device 331 * 332 * Input parameters: 333 * ctx - device context (includes ptr to our softc) 334 * inpstat - pointer to input status structure 335 * 336 * Return value: 337 * status, 0 = ok 338 ********************************************************************* */ 339 340static int 341et_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) 342{ 343 et_info_t *et = (et_info_t *) ctx->dev_softc; 344 int events; 345 346 events = et->etc->chops->getintrevents(et->etc->ch, FALSE); 347 inpstat->inp_status = ((events & INTR_RX) ? 1 : 0); 348 349 return 0; 350} 351 352/* ********************************************************************* 353 * ETHER_WRITE(ctx,buffer) 354 * 355 * Write a packet to the Ethernet device. 356 * 357 * Input parameters: 358 * ctx - device context (includes ptr to our softc) 359 * buffer - pointer to buffer descriptor. 360 * 361 * Return value: 362 * status, 0 = ok 363 ********************************************************************* */ 364 365static int 366et_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) 367{ 368 et_info_t *et = ctx->dev_softc; 369 void *p; 370 371 if (!(p = PKTGET(NULL, buffer->buf_length, TRUE))) { 372 ET_ERROR(("et%d: PKTGET failed\n", et->etc->unit)); 373 return CFE_ERR_NOMEM; 374 } 375 376 bcopy(buffer->buf_ptr, PKTDATA(NULL, p), buffer->buf_length); 377 378 ET_PRHDR("tx", (struct ether_header *) PKTDATA(NULL, p), PKTLEN(NULL, p), et->etc->unit); 379 ET_PRPKT("txpkt", PKTDATA(NULL, p), PKTLEN(NULL, p), et->etc->unit); 380 381 ASSERT(*et->etc->txavail > 0); 382 383 /* transmit the frame */ 384 et->etc->chops->tx(et->etc->ch, p); 385 386 /* wait for frame to complete */ 387 while (!(et->etc->chops->getintrevents(et->etc->ch, FALSE) & INTR_TX)); 388 389 /* reclaim any completed tx frames */ 390 et->etc->chops->txreclaim(et->etc->ch, FALSE); 391 392 return 0; 393} 394 395/* ********************************************************************* 396 * ETHER_IOCTL(ctx,buffer) 397 * 398 * Do device-specific I/O control operations for the device 399 * 400 * Input parameters: 401 * ctx - device context (includes ptr to our softc) 402 * buffer - pointer to buffer descriptor. 403 * 404 * Return value: 405 * status, 0 = ok 406 ********************************************************************* */ 407static int 408et_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) 409{ 410 et_info_t *et = (et_info_t *) ctx->dev_softc; 411 int val; 412 413 ET_TRACE(("et%d: et_ioctl: cmd 0x%x\n", et->etc->unit, buffer->buf_ioctlcmd)); 414 415 switch (buffer->buf_ioctlcmd) { 416 417 case IOCTL_ETHER_GETHWADDR: 418 bcopy(&et->etc->cur_etheraddr, buffer->buf_ptr, ETHER_ADDR_LEN); 419 break; 420 421 case IOCTL_ETHER_SETHWADDR: 422 bcopy(buffer->buf_ptr, &et->etc->cur_etheraddr, ETHER_ADDR_LEN); 423 et_init(et); 424 break; 425 426 case IOCTL_ETHER_GETSPEED: 427 val = ETHER_SPEED_UNKNOWN; 428 if (et->etc->linkstate) { 429 if (et->etc->speed == 10) 430 val = et->etc->duplex ? ETHER_SPEED_10FDX : ETHER_SPEED_10HDX; 431 else if (et->etc->speed == 100) 432 val = et->etc->duplex ? ETHER_SPEED_100FDX : ETHER_SPEED_100HDX; 433 } 434 *((int *) buffer->buf_ptr) = val; 435 break; 436 437 case IOCTL_ETHER_SETSPEED: 438 val = *((int *) buffer->buf_ptr); 439 if (val == ETHER_SPEED_AUTO) 440 val = ET_AUTO; 441 else if (val == ETHER_SPEED_10HDX) 442 val = ET_10HALF; 443 else if (val == ETHER_SPEED_10FDX) 444 val = ET_10FULL; 445 else if (val == ETHER_SPEED_100HDX) 446 val = ET_100HALF; 447 else if (val == ETHER_SPEED_100FDX) 448 val = ET_100FULL; 449 else 450 return CFE_ERR_UNSUPPORTED; 451 return etc_ioctl(et->etc, ETCSPEED, &val); 452 453 case IOCTL_ETHER_GETLINK: 454 *((int *) buffer->buf_ptr) = (int) et->etc->linkstate; 455 break; 456 457 case IOCTL_ETHER_GETLOOPBACK: 458 *((int *) buffer->buf_ptr) = et->etc->loopbk; 459 break; 460 461 case IOCTL_ETHER_SETLOOPBACK: 462 val = *((int *) buffer->buf_ptr); 463 if (val == ETHER_LOOPBACK_OFF) 464 val = (int) FALSE; 465 else 466 val = (int) TRUE; 467 return etc_ioctl(et->etc, ETCLOOP, &val); 468 469 default: 470 return CFE_ERR_UNSUPPORTED; 471 472 } 473 474 return 0; 475} 476 477/* ********************************************************************* 478 * ETHER_CLOSE(ctx) 479 * 480 * Close the Ethernet device. 481 * 482 * Input parameters: 483 * ctx - device context (includes ptr to our softc) 484 * 485 * Return value: 486 * status, 0 = ok 487 ********************************************************************* */ 488 489static int 490et_close(cfe_devctx_t *ctx) 491{ 492 et_info_t *et = (et_info_t *) ctx->dev_softc; 493 494 ET_TRACE(("et%d: et_close\n", et->etc->unit)); 495 496 return et_down(et, 1); 497} 498 499void 500et_intrson(et_info_t *et) 501{ 502 /* this is a polling driver - the chip intmask stays zero */ 503 ET_TRACE(("et%d: et_intrson\n", et->etc->unit)); 504} 505 506/* ********************************************************************* 507 * ETHER_POLL(ctx,ticks) 508 * 509 * Check for changes in the PHY, so we can track speed changes. 510 * 511 * Input parameters: 512 * ctx - device context (includes ptr to our softc) 513 * ticks- current time in ticks 514 * 515 * Return value: 516 * nothing 517 ********************************************************************* */ 518 519static void 520et_poll(cfe_devctx_t *ctx, int64_t ticks) 521{ 522 et_info_t *et = (et_info_t *) ctx->dev_softc; 523 524 if (TIMER_RUNNING(et->timer) && 525 TIMER_EXPIRED(et->timer)) { 526 etc_watchdog(et->etc); 527 TIMER_SET(et->timer, CFE_HZ / 2); 528 } 529} 530 531const static cfe_devdisp_t et_dispatch = { 532 et_open, 533 et_read, 534 et_inpstat, 535 et_write, 536 et_ioctl, 537 et_close, 538 et_poll, 539 NULL 540}; 541 542const cfe_driver_t bcmet = { 543 "Broadcom Ethernet", 544 "eth", 545 CFE_DEV_NETWORK, 546 &et_dispatch, 547 et_probe 548}; 549 550static int 551ui_cmd_et(ui_cmdline_t *cmdline, int argc, char *argv[]) 552{ 553 char *command, *name; 554 et_info_t *et; 555 cfe_device_t *dev; 556 int cmd, val, ret; 557 char *arg = (char *) &val; 558 559 if (!(command = cmd_getarg(cmdline, 0))) 560 return CFE_ERR_INV_PARAM; 561 562 if (!cmd_sw_value(cmdline, "-i", &name) || !name) 563 name = "eth0"; 564 if (!(dev = cfe_finddev(name)) || 565 !dev->dev_softc) 566 return CFE_ERR_DEVNOTFOUND; 567 for (et = et_list; et; et = et->next) 568 if (et == dev->dev_softc) 569 break; 570 if (!et && !(et = et_list)) 571 return CFE_ERR_DEVNOTFOUND; 572 573 if (!strcmp(command, "up")) 574 cmd = ETCUP; 575 else if (!strcmp(command, "down")) 576 cmd = ETCDOWN; 577 else if (!strcmp(command, "loop")) { 578 cmd = ETCLOOP; 579 arg = cmd_getarg(cmdline, 1); 580 } 581 else if (!strcmp(command, "dump")) { 582 if (!(arg = KMALLOC(4096, 0))) 583 return CFE_ERR_NOMEM; 584 bzero(arg, 4096); 585 if ((ret = etc_ioctl(et->etc, ETCDUMP, arg))) { 586 KFREE(arg); 587 return ret; 588 } 589 puts(arg); 590 KFREE(arg); 591 return 0; 592 } 593 else if (!strcmp(command, "msglevel")) { 594 cmd = ETCSETMSGLEVEL; 595 arg = cmd_getarg(cmdline, 1); 596 } 597 else if (!strcmp(command, "promisc")) { 598 cmd = ETCPROMISC; 599 arg = cmd_getarg(cmdline, 1); 600 } 601 else 602 return CFE_ERR_INV_PARAM; 603 604 if (!arg) 605 return CFE_ERR_INV_PARAM; 606 else if (arg != (char *) &val) { 607 val = bcm_strtoul(arg, NULL, 0); 608 arg = (char *) &val; 609 } 610 611 return etc_ioctl(et->etc, cmd, arg); 612} 613 614void 615et_addcmd(void) 616{ 617 cmd_addcmd("et", 618 ui_cmd_et, 619 NULL, 620 "Broadcom Ethernet utility.", 621 "et command [args..]\n\n" 622 "Configures the specified Broadcom Ethernet interface.", 623 "-i=*;Specifies the interface|" 624 "up;Activate the specified interface|" 625 "down;Deactivate the specified interface|" 626 "loop;Sets the loopback mode (0,1)|" 627 "dump;Dump driver information|" 628 "msglevel;Sets the driver message level|" 629 "promisc;Sets promiscuous mode|"); 630} 631