/* * CFE polled-mode device driver for * Broadcom BCM47XX 10/100 Mbps Ethernet Controller * * Copyright 2007, Broadcom Corporation * All Rights Reserved. * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; * the contents of this file may not be disclosed to third parties, copied * or duplicated in any form, in whole or in part, without the prior * written permission of Broadcom Corporation. * * $Id: et_cfe.c,v 1.1.1.1 2008/07/21 09:14:16 james26_jang Exp $ */ #include "lib_types.h" #include "lib_malloc.h" #include "lib_string.h" #include "lib_printf.h" #include "cfe_iocb.h" #include "cfe_device.h" #include "cfe_ioctl.h" #include "cfe_console.h" #include "cfe_timer.h" #include "cfe_error.h" #include "ui_command.h" #include #include #include #include #include #include #include #include #include #include #include typedef struct et_info { etc_info_t *etc; /* pointer to common os-independent data */ cfe_devctx_t *ctx; /* backpoint to device */ int64_t timer; /* one second watchdog timer */ osl_t *osh; struct et_info *next; /* pointer to next et_info_t in chain */ } et_info_t; static et_info_t *et_list = NULL; void et_init(et_info_t *et); void et_reset(et_info_t *et); void et_link_up(et_info_t *et); void et_link_down(et_info_t *et); int et_up(et_info_t *et); int et_down(et_info_t *et, int reset); void et_dump(et_info_t *et, struct bcmstrbuf *b); void et_addcmd(void); void et_intrson(et_info_t *et); void et_init(et_info_t *et) { ET_TRACE(("et%d: et_init\n", et->etc->unit)); etc_reset(et->etc); etc_init(et->etc); } void et_reset(et_info_t *et) { ET_TRACE(("et%d: et_reset\n", et->etc->unit)); etc_reset(et->etc); } void et_link_up(et_info_t *et) { ET_ERROR(("et%d: link up (%d%s)\n", et->etc->unit, et->etc->speed, (et->etc->duplex? "FD" : "HD"))); } void et_link_down(et_info_t *et) { ET_ERROR(("et%d: link down\n", et->etc->unit)); } int et_up(et_info_t *et) { if (et->etc->up) return 0; ET_TRACE(("et%d: et_up\n", et->etc->unit)); etc_up(et->etc); /* schedule one second watchdog timer */ TIMER_SET(et->timer, CFE_HZ / 2); return 0; } int et_down(et_info_t *et, int reset) { ET_TRACE(("et%d: et_down\n", et->etc->unit)); /* stop watchdog timer */ TIMER_CLEAR(et->timer); etc_down(et->etc, reset); return 0; } void et_dump(et_info_t *et, struct bcmstrbuf *b) { } et_info_t *et_phyfind(et_info_t *et, uint coreunit); uint16 et_phyrd(et_info_t *et, uint phyaddr, uint reg); void et_phywr(et_info_t *et, uint reg, uint phyaddr, uint16 val); /* * 47XX-specific shared mdc/mdio contortion: * Find the et associated with the same chip as * and coreunit matching . */ et_info_t * et_phyfind(et_info_t *et, uint coreunit) { et_info_t *tmp; /* walk the list et's */ for (tmp = et_list; tmp; tmp = tmp->next) { if (et->etc == NULL) continue; if (tmp->etc->coreunit != coreunit) continue; break; } return (tmp); } /* shared phy read entry point */ uint16 et_phyrd(et_info_t *et, uint phyaddr, uint reg) { return et->etc->chops->phyrd(et->etc->ch, phyaddr, reg); } /* shared phy write entry point */ void et_phywr(et_info_t *et, uint phyaddr, uint reg, uint16 val) { et->etc->chops->phywr(et->etc->ch, phyaddr, reg, val); } /* ********************************************************************* * ETHER_PROBE(drv,probe_a,probe_b,probe_ptr) * * Probe and install driver. * Create a context structure and attach to the * specified network device. * * Input parameters: * drv - driver descriptor * probe_a - device ID * probe_b - unit number * probe_ptr - mapped registers * * Return value: * nothing ********************************************************************* */ static void et_probe(cfe_driver_t *drv, unsigned long probe_a, unsigned long probe_b, void *probe_ptr) { et_info_t *et; uint16 device; uint unit; char name[128]; device = (uint16) probe_a; unit = (uint) probe_b; if (!(et = (et_info_t *) KMALLOC(sizeof(et_info_t), 0))) { ET_ERROR(("et%d: KMALLOC failed\n", unit)); return; } bzero(et, sizeof(et_info_t)); et->osh = osl_attach(et); ASSERT(et->osh); /* common load-time initialization */ if ((et->etc = etc_attach(et, VENDOR_BROADCOM, device, unit, et->osh, probe_ptr)) == NULL) { ET_ERROR(("et%d: etc_attach failed\n", unit)); KFREE(et); return; } /* this is a polling driver - the chip intmask stays zero */ et->etc->chops->intrsoff(et->etc->ch); /* add us to the global linked list */ et->next = et_list; et_list = et; /* print hello string */ et->etc->chops->longname(et->etc->ch, name, sizeof (name)); printf("et%d: %s %s\n", unit, name, EPI_VERSION_STR); cfe_attach(drv, et, NULL, name); } /* ********************************************************************* * ETHER_OPEN(ctx) * * Open the Ethernet device. The MAC is reset, initialized, and * prepared to receive and send packets. * * Input parameters: * ctx - device context (includes ptr to our softc) * * Return value: * status, 0 = ok ********************************************************************* */ static int et_open(cfe_devctx_t *ctx) { et_info_t *et = (et_info_t *) ctx->dev_softc; ET_TRACE(("et%d: et_open\n", et->etc->unit)); return et_up(et); } /* ********************************************************************* * ETHER_READ(ctx,buffer) * * Read a packet from the Ethernet device. If no packets are * available, the read will succeed but return 0 bytes. * * Input parameters: * ctx - device context (includes ptr to our softc) * buffer - pointer to buffer descriptor. * * Return value: * status, 0 = ok ********************************************************************* */ static int et_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) { et_info_t *et = (et_info_t *) ctx->dev_softc; int events; void *p; bcmenetrxh_t *rxh; uint16 flags; char eabuf[32]; ET_TRACE(("et%d: et_read\n", et->etc->unit)); /* assume no packets */ buffer->buf_retlen = 0; /* poll for packet */ events = et->etc->chops->getintrevents(et->etc->ch, FALSE); if ((events & INTR_RX) == 0) return 0; /* get packet */ if (!(p = et->etc->chops->rx(et->etc->ch))) goto done; /* packet buffer starts with rxhdr */ rxh = (bcmenetrxh_t *) PKTDATA(NULL, p); /* strip off rxhdr */ PKTPULL(NULL, p, HWRXOFF); /* check for reported frame errors */ flags = ltoh16(rxh->flags); if ((flags & (RXF_NO | RXF_RXER | RXF_CRC | RXF_OV))) { bcm_ether_ntoa((struct ether_addr *) (((struct ether_header *) PKTDATA(NULL, p))->ether_shost), eabuf); if (flags & RXF_NO) { ET_ERROR(("et%d: rx: crc error (odd nibbles) from %s\n", et->etc->unit, eabuf)); } if (flags & RXF_RXER) { ET_ERROR(("et%d: rx: symbol error from %s\n", et->etc->unit, eabuf)); } if (flags & RXF_CRC) { ET_ERROR(("et%d: rx: crc error from %s\n", et->etc->unit, eabuf)); } if (flags & RXF_OV) { ET_ERROR(("et%d: rx: fifo overflow\n", et->etc->unit)); } } else { bcopy(PKTDATA(NULL, p), buffer->buf_ptr, PKTLEN(NULL, p)); buffer->buf_retlen = PKTLEN(NULL, p); ET_PRHDR("rx", (struct ether_header *) buffer->buf_ptr, buffer->buf_retlen, et->etc->unit); ET_PRPKT("rxpkt", buffer->buf_ptr, buffer->buf_retlen, et->etc->unit); } /* free packet */ PKTFREE(et->osh, p, FALSE); done: /* post more rx bufs */ et->etc->chops->rxfill(et->etc->ch); return 0; } /* ********************************************************************* * ETHER_INPSTAT(ctx,inpstat) * * Check for received packets on the Ethernet device * * Input parameters: * ctx - device context (includes ptr to our softc) * inpstat - pointer to input status structure * * Return value: * status, 0 = ok ********************************************************************* */ static int et_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) { et_info_t *et = (et_info_t *) ctx->dev_softc; int events; events = et->etc->chops->getintrevents(et->etc->ch, FALSE); inpstat->inp_status = ((events & INTR_RX) ? 1 : 0); return 0; } /* ********************************************************************* * ETHER_WRITE(ctx,buffer) * * Write a packet to the Ethernet device. * * Input parameters: * ctx - device context (includes ptr to our softc) * buffer - pointer to buffer descriptor. * * Return value: * status, 0 = ok ********************************************************************* */ static int et_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) { et_info_t *et = ctx->dev_softc; void *p; if (!(p = PKTGET(NULL, buffer->buf_length, TRUE))) { ET_ERROR(("et%d: PKTGET failed\n", et->etc->unit)); return CFE_ERR_NOMEM; } bcopy(buffer->buf_ptr, PKTDATA(NULL, p), buffer->buf_length); ET_PRHDR("tx", (struct ether_header *) PKTDATA(NULL, p), PKTLEN(NULL, p), et->etc->unit); ET_PRPKT("txpkt", PKTDATA(NULL, p), PKTLEN(NULL, p), et->etc->unit); ASSERT(*et->etc->txavail > 0); /* transmit the frame */ et->etc->chops->tx(et->etc->ch, p); /* wait for frame to complete */ while (!(et->etc->chops->getintrevents(et->etc->ch, FALSE) & INTR_TX)); /* reclaim any completed tx frames */ et->etc->chops->txreclaim(et->etc->ch, FALSE); return 0; } /* ********************************************************************* * ETHER_IOCTL(ctx,buffer) * * Do device-specific I/O control operations for the device * * Input parameters: * ctx - device context (includes ptr to our softc) * buffer - pointer to buffer descriptor. * * Return value: * status, 0 = ok ********************************************************************* */ static int et_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) { et_info_t *et = (et_info_t *) ctx->dev_softc; int val; ET_TRACE(("et%d: et_ioctl: cmd 0x%x\n", et->etc->unit, buffer->buf_ioctlcmd)); switch (buffer->buf_ioctlcmd) { case IOCTL_ETHER_GETHWADDR: bcopy(&et->etc->cur_etheraddr, buffer->buf_ptr, ETHER_ADDR_LEN); break; case IOCTL_ETHER_SETHWADDR: bcopy(buffer->buf_ptr, &et->etc->cur_etheraddr, ETHER_ADDR_LEN); et_init(et); break; case IOCTL_ETHER_GETSPEED: val = ETHER_SPEED_UNKNOWN; if (et->etc->linkstate) { if (et->etc->speed == 10) val = et->etc->duplex ? ETHER_SPEED_10FDX : ETHER_SPEED_10HDX; else if (et->etc->speed == 100) val = et->etc->duplex ? ETHER_SPEED_100FDX : ETHER_SPEED_100HDX; } *((int *) buffer->buf_ptr) = val; break; case IOCTL_ETHER_SETSPEED: val = *((int *) buffer->buf_ptr); if (val == ETHER_SPEED_AUTO) val = ET_AUTO; else if (val == ETHER_SPEED_10HDX) val = ET_10HALF; else if (val == ETHER_SPEED_10FDX) val = ET_10FULL; else if (val == ETHER_SPEED_100HDX) val = ET_100HALF; else if (val == ETHER_SPEED_100FDX) val = ET_100FULL; else return CFE_ERR_UNSUPPORTED; return etc_ioctl(et->etc, ETCSPEED, &val); case IOCTL_ETHER_GETLINK: *((int *) buffer->buf_ptr) = (int) et->etc->linkstate; break; case IOCTL_ETHER_GETLOOPBACK: *((int *) buffer->buf_ptr) = et->etc->loopbk; break; case IOCTL_ETHER_SETLOOPBACK: val = *((int *) buffer->buf_ptr); if (val == ETHER_LOOPBACK_OFF) val = (int) FALSE; else val = (int) TRUE; return etc_ioctl(et->etc, ETCLOOP, &val); default: return CFE_ERR_UNSUPPORTED; } return 0; } /* ********************************************************************* * ETHER_CLOSE(ctx) * * Close the Ethernet device. * * Input parameters: * ctx - device context (includes ptr to our softc) * * Return value: * status, 0 = ok ********************************************************************* */ static int et_close(cfe_devctx_t *ctx) { et_info_t *et = (et_info_t *) ctx->dev_softc; ET_TRACE(("et%d: et_close\n", et->etc->unit)); return et_down(et, 1); } void et_intrson(et_info_t *et) { /* this is a polling driver - the chip intmask stays zero */ ET_TRACE(("et%d: et_intrson\n", et->etc->unit)); } /* ********************************************************************* * ETHER_POLL(ctx,ticks) * * Check for changes in the PHY, so we can track speed changes. * * Input parameters: * ctx - device context (includes ptr to our softc) * ticks- current time in ticks * * Return value: * nothing ********************************************************************* */ static void et_poll(cfe_devctx_t *ctx, int64_t ticks) { et_info_t *et = (et_info_t *) ctx->dev_softc; if (TIMER_RUNNING(et->timer) && TIMER_EXPIRED(et->timer)) { etc_watchdog(et->etc); TIMER_SET(et->timer, CFE_HZ / 2); } } const static cfe_devdisp_t et_dispatch = { et_open, et_read, et_inpstat, et_write, et_ioctl, et_close, et_poll, NULL }; const cfe_driver_t bcmet = { "Broadcom Ethernet", "eth", CFE_DEV_NETWORK, &et_dispatch, et_probe }; static int ui_cmd_et(ui_cmdline_t *cmdline, int argc, char *argv[]) { char *command, *name; et_info_t *et; cfe_device_t *dev; int cmd, val, ret; char *arg = (char *) &val; if (!(command = cmd_getarg(cmdline, 0))) return CFE_ERR_INV_PARAM; if (!cmd_sw_value(cmdline, "-i", &name) || !name) name = "eth0"; if (!(dev = cfe_finddev(name)) || !dev->dev_softc) return CFE_ERR_DEVNOTFOUND; for (et = et_list; et; et = et->next) if (et == dev->dev_softc) break; if (!et && !(et = et_list)) return CFE_ERR_DEVNOTFOUND; if (!strcmp(command, "up")) cmd = ETCUP; else if (!strcmp(command, "down")) cmd = ETCDOWN; else if (!strcmp(command, "loop")) { cmd = ETCLOOP; arg = cmd_getarg(cmdline, 1); } else if (!strcmp(command, "dump")) { if (!(arg = KMALLOC(4096, 0))) return CFE_ERR_NOMEM; bzero(arg, 4096); if ((ret = etc_ioctl(et->etc, ETCDUMP, arg))) { KFREE(arg); return ret; } puts(arg); KFREE(arg); return 0; } else if (!strcmp(command, "msglevel")) { cmd = ETCSETMSGLEVEL; arg = cmd_getarg(cmdline, 1); } else if (!strcmp(command, "promisc")) { cmd = ETCPROMISC; arg = cmd_getarg(cmdline, 1); } else return CFE_ERR_INV_PARAM; if (!arg) return CFE_ERR_INV_PARAM; else if (arg != (char *) &val) { val = bcm_strtoul(arg, NULL, 0); arg = (char *) &val; } return etc_ioctl(et->etc, cmd, arg); } void et_addcmd(void) { cmd_addcmd("et", ui_cmd_et, NULL, "Broadcom Ethernet utility.", "et command [args..]\n\n" "Configures the specified Broadcom Ethernet interface.", "-i=*;Specifies the interface|" "up;Activate the specified interface|" "down;Deactivate the specified interface|" "loop;Sets the loopback mode (0,1)|" "dump;Dump driver information|" "msglevel;Sets the driver message level|" "promisc;Sets promiscuous mode|"); }