2 * ADMtek switch setup functions
4 * Copyright (C) 2010, Broadcom Corporation
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
12 * $Id: etc_adm.c,v 1.27 2008-03-28 20:10:52 Exp $
18 #include <bcmendian.h>
19 #include <bcmparams.h>
23 /* Private state per ADM switch */
25 si_t
*sih
; /* SiliconBackplane handle */
26 void *vars
; /* variables handle */
27 uint coreidx
; /* Current core index */
28 uint32 eecs
, eesk
, eedi
; /* GPIO mapping */
31 /* Minimum timing constants */
32 #define EECK_EDGE_TIME 3 /* 3us - max(adm 2.5us, 93c 1us) */
33 #define EEDI_SETUP_TIME 1 /* 1us - max(adm 10ns, 93c 400ns) */
34 #define EECS_SETUP_TIME 1 /* 1us - max(adm no, 93c 200ns) */
36 /* Allocate private resource */
38 adm_attach(si_t
*sih
, char *vars
)
43 /* Allocate private data */
44 if (!(adm
= MALLOC(si_osh(sih
), sizeof(adm_info_t
)))) {
45 ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih
))));
48 bzero((char *) adm
, sizeof(adm_info_t
));
52 /* Init GPIO mapping. Default GPIO: 2, 3, 4 */
53 gpio
= getgpiopin(vars
, "adm_eecs", 2);
54 ET_ERROR(("adm_attach: got %d as adm_eecs", gpio
));
55 if (gpio
== GPIO_PIN_NOTDEFINED
) {
56 ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use"));
59 adm
->eecs
= 1 << gpio
;
61 gpio
= getgpiopin(vars
, "adm_eesk", 3);
62 ET_ERROR(("adm_attach: got %d as adm_eesk", gpio
));
63 if (gpio
== GPIO_PIN_NOTDEFINED
) {
64 ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use"));
67 adm
->eesk
= 1 << gpio
;
69 gpio
= getgpiopin(vars
, "adm_eedi", 4);
70 ET_ERROR(("adm_attach: got %d as adm_eedi", gpio
));
71 if (gpio
== GPIO_PIN_NOTDEFINED
) {
72 ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use"));
75 adm
->eedi
= 1 << gpio
;
84 /* Release private resource */
86 adm_detach(adm_info_t
*adm
)
88 /* Free private data */
89 MFREE(si_osh(adm
->sih
), adm
, sizeof(adm_info_t
));
93 * The following local functions provide chip access control. The
94 * general rules in writing these supporting routines are:
96 * 1. EECS should be kept low after each routine.
97 * 2. EESK should be kept low after each routine.
99 /* Enable register access to the chip */
101 adm_enable(adm_info_t
*adm
)
105 /* Save current core index */
106 adm
->coreidx
= si_coreidx(adm
->sih
);
108 /* Switch to GPIO core for faster access */
109 regs
= si_gpiosetcore(adm
->sih
);
113 /* Disable register access to the chip */
115 adm_disable(adm_info_t
*adm
)
117 /* Switch back to original core */
118 si_setcoreidx(adm
->sih
, adm
->coreidx
);
121 /* Enable outputs with specified value to the chip */
123 adm_enout(adm_info_t
*adm
, uint32 pins
, uint val
)
125 /* Prepare GPIO output value */
126 si_gpioout(adm
->sih
, pins
, val
, GPIO_DRV_PRIORITY
);
127 /* Enable GPIO outputs */
128 si_gpioouten(adm
->sih
, pins
, pins
, GPIO_DRV_PRIORITY
);
129 OSL_DELAY(EECK_EDGE_TIME
);
132 /* Disable outputs to the chip */
134 adm_disout(adm_info_t
*adm
, uint32 pins
)
136 /* Disable GPIO outputs */
137 si_gpioouten(adm
->sih
, pins
, 0, GPIO_DRV_PRIORITY
);
138 OSL_DELAY(EECK_EDGE_TIME
);
141 /* Advance clock(s) */
143 adm_adclk(adm_info_t
*adm
, int clocks
)
146 for (i
= 0; i
< clocks
; i
++) {
148 si_gpioout(adm
->sih
, adm
->eesk
, adm
->eesk
, GPIO_DRV_PRIORITY
);
149 OSL_DELAY(EECK_EDGE_TIME
);
151 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
152 OSL_DELAY(EECK_EDGE_TIME
);
156 /* Write a bit stream to the chip */
158 adm_write(adm_info_t
*adm
, int cs
, uint8
*buf
, uint bits
)
160 uint i
, len
= (bits
+ 7) / 8;
165 si_gpioout(adm
->sih
, adm
->eecs
, adm
->eecs
, GPIO_DRV_PRIORITY
);
167 si_gpioout(adm
->sih
, adm
->eecs
, 0, GPIO_DRV_PRIORITY
);
168 OSL_DELAY(EECK_EDGE_TIME
);
170 /* Byte assemble from MSB to LSB */
171 for (i
= 0; i
< len
; i
++) {
172 /* Bit bang from MSB to LSB */
173 for (mask
= 0x80; mask
&& bits
> 0; mask
>>= 1, bits
--) {
175 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
176 OSL_DELAY(EECK_EDGE_TIME
);
178 /* Output on rising edge */
180 si_gpioout(adm
->sih
, adm
->eedi
, adm
->eedi
, GPIO_DRV_PRIORITY
);
182 si_gpioout(adm
->sih
, adm
->eedi
, 0, GPIO_DRV_PRIORITY
);
183 OSL_DELAY(EEDI_SETUP_TIME
);
186 si_gpioout(adm
->sih
, adm
->eesk
, adm
->eesk
, GPIO_DRV_PRIORITY
);
187 OSL_DELAY(EECK_EDGE_TIME
);
192 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
193 OSL_DELAY(EECK_EDGE_TIME
);
197 si_gpioout(adm
->sih
, adm
->eecs
, 0, GPIO_DRV_PRIORITY
);
200 /* Handy macros for writing fixed length values */
201 #define adm_write8(adm, cs, b) { uint8 val = (uint8) (b); adm_write(adm, cs, &val, sizeof(val)*8); }
202 #define adm_write16(adm, cs, w) { uint16 val = hton16(w); \
203 adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }
204 #define adm_write32(adm, cs, i) { uint32 val = hton32(i); \
205 adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }
207 /* Write chip configuration register */
208 /* Follow 93c66 timing and chip's min EEPROM timing requirement */
210 adm_wreg(adm_info_t
*adm
, uint8 addr
, uint16 val
)
212 /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
214 (0x05 << 5) | (addr
>> 3),
215 (addr
<< 5) | (uint8
)(val
>> 11),
220 ET_TRACE(("adm_wreg: addr %02x val %04x (%02X%02X%02X%02X)\n",
221 addr
, val
, bits
[0], bits
[1], bits
[2], bits
[3]));
223 /* Enable GPIO outputs with all pins to 0 */
224 adm_enout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
, 0);
226 /* Write cmd. Total 27 bits */
227 adm_write(adm
, 1, bits
, 27);
229 /* Extra clock(s) required per datasheet */
232 /* Disable GPIO outputs */
233 adm_disout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
);
236 /* Configure the chip based on nvram settings */
238 adm_config_vlan(adm_info_t
*adm
)
240 /* Port configuration */
242 uint8 addr
; /* port configuration register */
243 uint16 vlan
; /* vlan port mapping */
244 uint8 tagged
; /* output tagging */
245 uint8 cpu
; /* cpu port? 1 - yes, 0 - no */
246 uint16 pvid
; /* cpu port pvid */
253 #if defined(PMON) || defined(_CFE_)
254 {9, 1<<8, 0, 1, -1} /* no output tagging for pmon/cfe */
255 #else /* #if defined(PMON) || defined(CFE) */
256 {9, 1<<8, 1, 1, -1} /* output tagging for linux... */
257 #endif /* #if defined(PMON) || defined(CFE) */
259 /* Vlan ports bitmap */
261 uint8 addr
; /* vlan port map register */
282 /* Enable access to the switch */
285 /* vlan mode select register (0x11): vlan on, mac clone */
286 adm_wreg(adm
, 0x11, 0xff30);
288 /* vlan port group: port configuration, vlan port map */
289 /* VLAN ID is equal to vlan number, max 16 vlans */
290 for (vid
= 0; vid
< 16; vid
++) {
291 char port
[] = "XXXX", *next
, *ports
, *cur
;
292 char vlanports
[] = "vlanXXXXports";
297 /* no members if VLAN id is out of limitation */
298 if (vid
> VLAN_MAXVID
)
301 /* get nvram port settings */
302 sprintf(vlanports
, "vlan%dports", vid
);
303 ports
= getvar(adm
->vars
, vlanports
);
305 /* disable this vlan if not defined */
310 * port configuration register (0x01, 0x03, 0x05, 0x07, 0x08, 0x09):
311 * input/output tagging, pvid, auto mdix, auto negotiation, ...
312 * cpu port needs special handing to support pmon/cfe/linux...
314 for (cur
= ports
; cur
; cur
= next
) {
315 /* tokenize the port list */
318 next
= bcmstrstr(cur
, " ");
319 len
= next
? next
- cur
: strlen(cur
);
322 if (len
> sizeof(port
) - 1)
323 len
= sizeof(port
) - 1;
324 strncpy(port
, cur
, len
);
327 /* make sure port # is within the range */
328 port_num
= bcm_atoi(port
);
329 if (port_num
>= sizeof(port_cfg_tab
) / sizeof(port_cfg_tab
[0])) {
330 ET_ERROR(("port number %d is out of range\n", port_num
));
334 /* build vlan port map */
335 vlan_map
|= port_cfg_tab
[port_num
].vlan
;
337 /* cpu port needs special care */
338 if (port_cfg_tab
[port_num
].cpu
) {
339 /* cpu port's default VLAN is lan! */
340 if (strchr(port
, '*'))
341 port_cfg_tab
[port_num
].pvid
= vid
;
342 /* will be done later */
347 port_cfg
= 0x8000 | /* auto mdix */
348 (vid
<< 10) | /* pvid */
349 0x000f; /* full duplex, 100Mbps, auto neg, flow ctrl */
350 adm_wreg(adm
, port_cfg_tab
[port_num
].addr
, port_cfg
);
353 /* vlan port map register (0x13 - 0x22) */
354 adm_wreg(adm
, vlan_cfg_tab
[vid
].addr
, vlan_map
);
357 /* cpu port config: auto mdix, pvid, output tagging, ... */
358 for (i
= 0; i
< sizeof(port_cfg_tab
)/sizeof(port_cfg_tab
[0]); i
++) {
363 if (port_cfg_tab
[i
].cpu
== 0 || port_cfg_tab
[i
].pvid
== 0xffff)
367 tagged
= port_cfg_tab
[i
].tagged
? 1 : 0;
368 pvid
= port_cfg_tab
[i
].pvid
;
369 port_cfg
= 0x8000 | /* auto mdix */
370 (pvid
<< 10) | /* pvid */
371 (tagged
<< 4) | /* output tagging */
372 0x000f; /* full duplex, 100Mbps, auto neg, flow ctrl */
373 adm_wreg(adm
, port_cfg_tab
[i
].addr
, port_cfg
);
376 /* Disable access to the switch */
383 * Enable the chip with preset default configuration:
385 * TP Auto MDIX (EESK/GPIO = 1)
386 * Single Color LED (EEDI/GPIO = 0)
387 * EEPROM Disable (H/W pull down)
390 adm_enable_device(adm_info_t
*adm
)
395 /* Check nvram override existance */
396 if ((rc
= getgpiopin(adm
->vars
, "adm_rc", GPIO_PIN_NOTDEFINED
)) == GPIO_PIN_NOTDEFINED
)
400 /* Enable access to the switch */
403 * Reset sequence: RC high->low(100ms)->high(30ms)
405 * WAR: Certain boards don't have the correct power on
406 * reset logic therefore we must explicitly perform the
407 * sequece in software.
409 /* Keep RC high for at least 20ms */
410 adm_enout(adm
, rc
, rc
);
411 for (i
= 0; i
< 20; i
++)
413 /* Keep RC low for at least 100ms */
414 adm_enout(adm
, rc
, 0);
415 for (i
= 0; i
< 100; i
++)
417 /* Set default configuration */
418 adm_enout(adm
, adm
->eesk
| adm
->eedi
, adm
->eesk
);
419 /* Keep RC high for at least 30ms */
420 adm_enout(adm
, rc
, rc
);
421 for (i
= 0; i
< 30; i
++)
423 /* Leave RC high and disable GPIO outputs */
424 adm_disout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
);
425 /* Disable access to the switch */
431 /* Read a bit stream from the chip */
433 adm_read(adm_info_t
*adm
, int cs
, uint32 pin
, uint8
*buf
, uint bits
)
435 uint i
, len
= (bits
+ 7) / 8;
439 si_gpioout(adm
->sih
, adm
->eecs
, adm
->eecs
, GPIO_DRV_PRIORITY
);
441 si_gpioout(adm
->sih
, adm
->eecs
, 0, GPIO_DRV_PRIORITY
);
442 OSL_DELAY(EECK_EDGE_TIME
);
444 /* Byte assemble from MSB to LSB */
445 for (i
= 0; i
< len
; i
++) {
446 uint8 mask
, byte
= 0;
447 /* Bit bang from MSB to LSB */
448 for (mask
= 0x80; mask
&& bits
> 0; mask
>>= 1, bits
--) {
450 si_gpioout(adm
->sih
, adm
->eesk
, adm
->eesk
, GPIO_DRV_PRIORITY
);
451 OSL_DELAY(EECK_EDGE_TIME
);
453 /* Sample on rising edge */
454 if (si_gpioin(adm
->sih
) & pin
)
458 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
459 OSL_DELAY(EECK_EDGE_TIME
);
466 si_gpioout(adm
->sih
, adm
->eecs
, 0, GPIO_DRV_PRIORITY
);
469 /* Handy macros for reading fixed length values */
470 #define adm_read8(adm, cs, pin, b) { uint8 val; \
471 adm_read(adm, cs, pin, &val, sizeof(val) * 8); *(b) = val; }
472 #define adm_read16(adm, cs, pin, w) { uint16 val; \
473 adm_read(adm, cs, pin, (uint8 *)&val, sizeof(val) * 8); *(w) = ntoh16(val); }
474 #define adm_read32(adm, cs, pin, i) { uint32 val; \
475 adm_read(adm, cs, pin, (uint8 *)&val, sizeof(val) * 8); *(i) = ntoh32(val); }
477 /* Read counter/config register. table 0 - config registers, 1 - internal counters */
478 /* Follow chip's SMI timing */
480 adm_rreg(adm_info_t
*adm
, int addr
, int table
)
482 /* Command: preamble(11) + start(01) + opcode(10) + table(b) +
483 * device(00) + register(bbbbbbb)
485 uint16 cmd
= (3 << 14) | (1 << 12) | (2 << 10) | (table
<< 9) | (0 << 7) | addr
;
488 /* Enable GPIO outputs */
489 adm_enout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
, 0);
491 /* Preamble: at lease 32 bit 1s */
492 adm_write32(adm
, 0, 0xffffffff);
494 /* Command: 2 extra preamble bits plus 14 command bits */
495 adm_write16(adm
, 0, cmd
);
497 /* Z EEDI: the switch will drive it */
498 adm_disout(adm
, adm
->eedi
);
500 /* Turn around: 1 bit */
503 /* Register value: 32 bits */
504 adm_read32(adm
, 0, adm
->eedi
, &data
);
506 /* Idle: at least 1 extra clock */
509 /* Disable GPIO outputs */
510 adm_disout(adm
, adm
->eecs
| adm
->eesk
);
516 adm_dump_regs(adm_info_t
*adm
, char *buf
)
521 /* enable access to the switch */
524 /* dump admtek switch registers */
525 buf
+= sprintf(buf
, "admtek:\n");
526 for (i
= 0; i
<= 0x2d; i
++) {
527 v
= adm_rreg(adm
, i
, 0);
528 buf
+= sprintf(buf
, "%04x ",
529 ((i
% 2) ? ((v
>> 16) & 0xffff) : (v
& 0xffff)));
531 buf
+= sprintf(buf
, "\n");
533 buf
+= sprintf(buf
, "\n");
535 /* disable access to the switch */