1/*
2 * Common [OS-independent] portion of
3 * Broadcom Home Networking Division 10/100 Mbit/s Ethernet
4 * Device Driver.
5 *
6 * Copyright 2007, Broadcom Corporation
7 * All Rights Reserved.
8 *
9 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
10 * the contents of this file may not be disclosed to third parties, copied
11 * or duplicated in any form, in whole or in part, without the prior
12 * written permission of Broadcom Corporation.
13 * $Id: etc.c,v 1.1.1.1 2008/10/15 03:25:54 james26_jang Exp $
14 */
15
16#include <typedefs.h>
17#include <osl.h>
18#include <bcmendian.h>
19#include <proto/ethernet.h>
20#include <proto/vlan.h>
21#include <proto/bcmip.h>
22#include <bcmenetmib.h>
23#include <bcmenetrxh.h>
24#include <bcmenetphy.h>
25#include <et_dbg.h>
26#include <etc.h>
27#include <et_export.h>
28#include <bcmutils.h>
29
30#ifdef ETROBO
31#ifndef	_sbutils_h_
32typedef const struct sb_pub  sb_t;
33#endif
34#include <bcmrobo.h>
35#endif /* ETROBO */
36
37uint32 et_msg_level =
38	0;
39
40/* local prototypes */
41static void etc_loopback(etc_info_t *etc, int on);
42
43/* find the chip opsvec for this chip */
44struct chops*
45etc_chipmatch(uint vendor, uint device)
46{
47	{
48	extern struct chops bcm47xx_et_chops;
49	if (bcm47xx_et_chops.id(vendor, device))
50		return (&bcm47xx_et_chops);
51	}
52	return (NULL);
53}
54
55void*
56etc_attach(void *et, uint vendor, uint device, uint unit, void *osh, void *regsva)
57{
58	etc_info_t *etc;
59
60	ET_TRACE(("et%d: etc_attach: vendor 0x%x device 0x%x\n", unit, vendor, device));
61
62	/* some code depends on packed structures */
63	ASSERT(sizeof(struct ether_addr) == ETHER_ADDR_LEN);
64	ASSERT(sizeof(struct ether_header) == ETHER_HDR_LEN);
65
66	/* allocate etc_info_t state structure */
67	if ((etc = (etc_info_t*) MALLOC(osh, sizeof(etc_info_t))) == NULL) {
68		ET_ERROR(("et%d: etc_attach: out of memory, malloced %d bytes\n", unit,
69		          MALLOCED(osh)));
70		return (NULL);
71	}
72	bzero((char*)etc, sizeof(etc_info_t));
73
74	etc->et = et;
75	etc->unit = unit;
76	etc->osh = osh;
77	etc->vendorid = (uint16) vendor;
78	etc->deviceid = (uint16) device;
79	etc->forcespeed = ET_AUTO;
80	etc->linkstate = FALSE;
81
82	/* set chip opsvec */
83	etc->chops = etc_chipmatch(vendor, device);
84	ASSERT(etc->chops);
85
86	/* chip attach */
87	if ((etc->ch = (*etc->chops->attach)(etc, osh, regsva)) == NULL) {
88		ET_ERROR(("et%d: chipattach error\n", unit));
89		goto fail;
90	}
91
92	return ((void*)etc);
93
94fail:
95	etc_detach(etc);
96	return (NULL);
97}
98
99void
100etc_detach(etc_info_t *etc)
101{
102	if (etc == NULL)
103		return;
104
105	/* free chip private state */
106	if (etc->ch) {
107		(*etc->chops->detach)(etc->ch);
108		etc->chops = etc->ch = NULL;
109	}
110
111	MFREE(etc->osh, etc, sizeof(etc_info_t));
112}
113
114void
115etc_reset(etc_info_t *etc)
116{
117	ET_TRACE(("et%d: etc_reset\n", etc->unit));
118
119	etc->reset++;
120
121	/* reset the chip */
122	(*etc->chops->reset)(etc->ch);
123
124	/* free any posted tx packets */
125	(*etc->chops->txreclaim)(etc->ch, TRUE);
126
127#ifdef DMA
128	/* free any posted rx packets */
129	(*etc->chops->rxreclaim)(etc->ch);
130#endif /* DMA */
131}
132
133void
134etc_init(etc_info_t *etc)
135{
136	ET_TRACE(("et%d: etc_init\n", etc->unit));
137
138	ASSERT(etc->pioactive == NULL);
139	ASSERT(!ETHER_ISNULLADDR(&etc->cur_etheraddr));
140	ASSERT(!ETHER_ISMULTI(&etc->cur_etheraddr));
141
142	/* init the chip */
143	(*etc->chops->init)(etc->ch, TRUE);
144}
145
146/* mark interface up */
147void
148etc_up(etc_info_t *etc)
149{
150	etc->up = TRUE;
151
152	et_init(etc->et);
153}
154
155/* mark interface down */
156uint
157etc_down(etc_info_t *etc, int reset)
158{
159	uint callback;
160
161	callback = 0;
162
163	etc->up = FALSE;
164	if (reset)
165		et_reset(etc->et);
166
167	/* suppress link state changes during power management mode changes */
168	if (etc->linkstate) {
169		etc->linkstate = FALSE;
170		if (!etc->pm_modechange)
171			et_link_down(etc->et);
172	}
173
174	return (callback);
175}
176
177unsigned int etc_arl_dump(etc_info_t *etc, char *buff, int maxno)
178{
179	uint8 val;
180	uint8 res_reg[8];
181	uint8 res_ext_reg;
182	int size=0;
183	int offset=0;
184	int count=0;
185	int port_id = -1;
186
187	robo_info_t *robo = (robo_info_t *)etc->robo;
188
189	//printk(KERN_ERR "etc_arl_dump\n");
190
191	if (etc->robo)
192	{
193		robo->ops->read_reg(etc->robo, 0x04, 0x0, &val, 1);
194		// ARL Search Control Register page=5 addr=0x20
195		// start
196		val = 0x80;
197		robo->ops->write_reg(etc->robo, 0x05, 0x20, &val, 1);
198		while(1)
199		{
200			robo->ops->read_reg(etc->robo, 0x05, 0x20, &val, 1);
201			if(val&0x80)
202			{
203				if(val&0x01)
204				{
205					robo->ops->read_reg(etc->robo, 0x05, 0x2c, &res_ext_reg, 1);
206					robo->ops->read_reg(etc->robo, 0x05, 0x24, res_reg, 8);
207					port_id = -1;
208					port_id = (int)res_reg[6];
209					if(port_id < 0 || port_id > 3)
210						continue;
211					size = sprintf(buff+offset, "%02x:%02x:%02x:%02x:%02x:%02x  %02x  %02x  %02x\n", res_reg[5], res_reg[4], res_reg[3], res_reg[2], res_reg[1], res_reg[0], res_reg[6], res_reg[7], res_ext_reg);
212					//printk("rreg: %s\n", buff+offset);
213					offset+=size;
214					count++;
215					if(count>=maxno) break;
216				}
217			}
218			else break;
219		}
220	}
221	//printk("%x %x %x %x\n", offset, count, maxno, *len);
222	return offset;
223}
224
225/* common ioctl handler.  return: 0=ok, -1=error */
226int
227etc_ioctl(etc_info_t *etc, int cmd, void *arg)
228{
229	int error;
230	int val;
231	int *vec = (int*)arg;
232
233	error = 0;
234
235	val = arg ? *(int*)arg : 0;
236
237	ET_TRACE(("et%d: etc_ioctl: cmd 0x%x\n", etc->unit, cmd));
238
239	switch (cmd) {
240	case ETCUP:
241		et_up(etc->et);
242		break;
243
244	case ETCDOWN:
245		et_down(etc->et, TRUE);
246		break;
247
248	case ETCLOOP:
249		etc_loopback(etc, val);
250		break;
251
252	case ETCDUMP:
253		if (et_msg_level & 0x10000)
254			bcmdumplog((char *)arg, 4096);
255		break;
256
257	case ETCSETMSGLEVEL:
258		et_msg_level = val;
259		break;
260
261	case ETCPROMISC:
262		etc_promisc(etc, val);
263		break;
264
265	case ETCQOS:
266		etc_qos(etc, val);
267		break;
268
269	case ETCSPEED:
270		if ((val != ET_AUTO) && (val != ET_10HALF) && (val != ET_10FULL) &&
271		    (val != ET_100HALF) && (val != ET_100FULL))
272			goto err;
273		etc->forcespeed = val;
274
275		/* explicitly reset the phy */
276		(*etc->chops->phyreset)(etc->ch, etc->phyaddr);
277
278		/* request restart autonegotiation if we're reverting to adv mode */
279		if ((etc->forcespeed == ET_AUTO) & etc->advertise)
280			etc->needautoneg = TRUE;
281
282		et_init(etc->et);
283		break;
284
285	case ETCPHYRD:
286		if (vec) {
287			vec[1] = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, vec[0]);
288			ET_TRACE(("etc_ioctl: ETCPHYRD of reg 0x%x => 0x%x\n", vec[0], vec[1]));
289		}
290		break;
291
292	case ETCPHYRD2:
293		if (vec) {
294			uint phyaddr, reg;
295			phyaddr = vec[0] >> 16;
296			if (phyaddr < MAXEPHY) {
297				reg = vec[0] & 0xffff;
298				vec[1] = (*etc->chops->phyrd)(etc->ch, phyaddr, reg);
299				ET_TRACE(("etc_ioctl: ETCPHYRD2 of phy 0x%x, reg 0x%x => 0x%x\n",
300				          phyaddr, reg, vec[1]));
301			}
302		}
303		break;
304
305	case ETCPHYWR:
306		if (vec) {
307			ET_TRACE(("etc_ioctl: ETCPHYWR to reg 0x%x <= 0x%x\n", vec[0], vec[1]));
308			(*etc->chops->phywr)(etc->ch, etc->phyaddr, vec[0], (uint16)vec[1]);
309		}
310		break;
311
312	case ETCPHYWR2:
313		if (vec) {
314			uint phyaddr, reg;
315			phyaddr = vec[0] >> 16;
316			if (phyaddr < MAXEPHY) {
317				reg = vec[0] & 0xffff;
318				(*etc->chops->phywr)(etc->ch, phyaddr, reg, (uint16)vec[1]);
319				ET_TRACE(("etc_ioctl: ETCPHYWR2 to phy 0x%x, reg 0x%x <= 0x%x\n",
320				          phyaddr, reg, vec[1]));
321			}
322		}
323		break;
324
325#ifdef ETROBO
326	case ETCROBORD:
327		if (etc->robo && vec) {
328			uint page, reg;
329			uint16 val;
330			robo_info_t *robo = (robo_info_t *)etc->robo;
331
332			page = vec[0] >> 16;
333			reg = vec[0] & 0xffff;
334			val = -1;
335			robo->ops->read_reg(etc->robo, page, reg, &val, 2);
336			vec[1] = val;
337			ET_TRACE(("etc_ioctl: ETCROBORD of page 0x%x, reg 0x%x => 0x%x\n",
338			          page, reg, val));
339		}
340		break;
341
342	case ETCROBOWR:
343		if (etc->robo && vec) {
344			uint page, reg;
345			uint16 val;
346			robo_info_t *robo = (robo_info_t *)etc->robo;
347
348			page = vec[0] >> 16;
349			reg = vec[0] & 0xffff;
350			val = vec[1];
351			robo->ops->write_reg(etc->robo, page, vec[0], &val, 2);
352			ET_TRACE(("etc_ioctl: ETCROBOWR to page 0x%x, reg 0x%x <= 0x%x\n",
353			          page, reg, val));
354		}
355		break;
356#endif /* ETROBO */
357
358
359	default:
360	err:
361		error = -1;
362	}
363
364	return (error);
365}
366
367/* called once per second */
368void
369etc_watchdog(etc_info_t *etc)
370{
371	uint16 status;
372	uint16 adv;
373	uint16 lpa;
374
375	etc->now++;
376
377	/* no local phy registers */
378	if (etc->phyaddr == EPHY_NOREG) {
379		etc->linkstate = TRUE;
380		etc->speed = 100;
381		etc->duplex = 1;
382		return;
383	}
384
385	status = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 1);
386	adv = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 4);
387	lpa = (*etc->chops->phyrd)(etc->ch, etc->phyaddr, 5);
388
389	/* check for bad mdio read */
390	if (status == 0xffff) {
391		ET_ERROR(("et%d: etc_watchdog: bad mdio read: phyaddr %d mdcport %d\n",
392			etc->unit, etc->phyaddr, etc->mdcport));
393		return;
394	}
395
396	/* monitor link state */
397	if (!etc->linkstate && (status & STAT_LINK)) {
398		etc->linkstate = TRUE;
399
400		if (etc->pm_modechange)
401			etc->pm_modechange = FALSE;
402		else
403			et_link_up(etc->et);
404	}
405	else if (etc->linkstate && !(status & STAT_LINK)) {
406		etc->linkstate = FALSE;
407		if (!etc->pm_modechange)
408			et_link_down(etc->et);
409	}
410
411	/* update current speed and duplex */
412	if ((adv & ADV_100FULL) && (lpa & LPA_100FULL)) {
413		etc->speed = 100;
414		etc->duplex = 1;
415	} else if ((adv & ADV_100HALF) && (lpa & LPA_100HALF)) {
416		etc->speed = 100;
417		etc->duplex = 0;
418	} else if ((adv & ADV_10FULL) && (lpa & LPA_10FULL)) {
419		etc->speed = 10;
420		etc->duplex = 1;
421	} else {
422		etc->speed = 10;
423		etc->duplex = 0;
424	}
425
426	/* keep emac txcontrol duplex bit consistent with current phy duplex */
427	(*etc->chops->duplexupd)(etc->ch);
428
429	/* check for remote fault error */
430	if (status & STAT_REMFAULT) {
431		ET_ERROR(("et%d: remote fault\n", etc->unit));
432	}
433
434	/* check for jabber error */
435	if (status & STAT_JAB) {
436		ET_ERROR(("et%d: jabber\n", etc->unit));
437	}
438
439	/*
440	 * Read chip mib counters occationally before the 16bit ones can wrap.
441	 * We don't use the high-rate mib counters.
442	 */
443	if ((etc->now % 30) == 0)
444		(*etc->chops->statsupd)(etc->ch);
445}
446
447static void
448etc_loopback(etc_info_t *etc, int on)
449{
450	ET_TRACE(("et%d: etc_loopback: %d\n", etc->unit, on));
451
452	etc->loopbk = (bool) on;
453	et_init(etc->et);
454}
455
456void
457etc_promisc(etc_info_t *etc, uint on)
458{
459	ET_TRACE(("et%d: etc_promisc: %d\n", etc->unit, on));
460
461	etc->promisc = (bool) on;
462	et_init(etc->et);
463}
464
465void
466etc_qos(etc_info_t *etc, uint on)
467{
468	ET_TRACE(("et%d: etc_qos: %d\n", etc->unit, on));
469
470	etc->qos = (bool) on;
471	et_init(etc->et);
472}
473
474
475uint
476etc_totlen(etc_info_t *etc, void *p)
477{
478	uint total;
479
480	total = 0;
481	for (; p; p = PKTNEXT(etc->osh, p))
482		total += PKTLEN(etc->osh, p);
483	return (total);
484}
485
486