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