2 * ADMtek switch setup functions
4 * Copyright (C) 2009, 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 $
19 #include <bcmendian.h>
20 #include <bcmparams.h>
24 /* Private state per ADM switch */
26 si_t
*sih
; /* SiliconBackplane handle */
27 void *vars
; /* variables handle */
28 uint coreidx
; /* Current core index */
29 uint32 eecs
, eesk
, eedi
; /* GPIO mapping */
32 /* Minimum timing constants */
33 #define EECK_EDGE_TIME 3 /* 3us - max(adm 2.5us, 93c 1us) */
34 #define EEDI_SETUP_TIME 1 /* 1us - max(adm 10ns, 93c 400ns) */
35 #define EECS_SETUP_TIME 1 /* 1us - max(adm no, 93c 200ns) */
37 /* Allocate private resource */
39 adm_attach(si_t
*sih
, char *vars
)
44 /* Allocate private data */
45 if (!(adm
= MALLOC(si_osh(sih
), sizeof(adm_info_t
)))) {
46 ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih
))));
49 bzero((char *) adm
, sizeof(adm_info_t
));
53 /* Init GPIO mapping. Default GPIO: 2, 3, 4 */
54 gpio
= getgpiopin(vars
, "adm_eecs", 2);
55 ET_ERROR(("adm_attach: got %d as adm_eecs", gpio
));
56 if (gpio
== GPIO_PIN_NOTDEFINED
) {
57 ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use"));
60 adm
->eecs
= 1 << gpio
;
62 gpio
= getgpiopin(vars
, "adm_eesk", 3);
63 ET_ERROR(("adm_attach: got %d as adm_eesk", gpio
));
64 if (gpio
== GPIO_PIN_NOTDEFINED
) {
65 ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use"));
68 adm
->eesk
= 1 << gpio
;
70 gpio
= getgpiopin(vars
, "adm_eedi", 4);
71 ET_ERROR(("adm_attach: got %d as adm_eedi", gpio
));
72 if (gpio
== GPIO_PIN_NOTDEFINED
) {
73 ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use"));
76 adm
->eedi
= 1 << gpio
;
85 /* Release private resource */
87 adm_detach(adm_info_t
*adm
)
89 /* Free private data */
90 MFREE(si_osh(adm
->sih
), adm
, sizeof(adm_info_t
));
94 * The following local functions provide chip access control. The
95 * general rules in writing these supporting routines are:
97 * 1. EECS should be kept low after each routine.
98 * 2. EESK should be kept low after each routine.
100 /* Enable register access to the chip */
102 adm_enable(adm_info_t
*adm
)
106 /* Save current core index */
107 adm
->coreidx
= si_coreidx(adm
->sih
);
109 /* Switch to GPIO core for faster access */
110 regs
= si_gpiosetcore(adm
->sih
);
114 /* Disable register access to the chip */
116 adm_disable(adm_info_t
*adm
)
118 /* Switch back to original core */
119 si_setcoreidx(adm
->sih
, adm
->coreidx
);
122 /* Enable outputs with specified value to the chip */
124 adm_enout(adm_info_t
*adm
, uint32 pins
, uint val
)
126 /* Prepare GPIO output value */
127 si_gpioout(adm
->sih
, pins
, val
, GPIO_DRV_PRIORITY
);
128 /* Enable GPIO outputs */
129 si_gpioouten(adm
->sih
, pins
, pins
, GPIO_DRV_PRIORITY
);
130 OSL_DELAY(EECK_EDGE_TIME
);
133 /* Disable outputs to the chip */
135 adm_disout(adm_info_t
*adm
, uint32 pins
)
137 /* Disable GPIO outputs */
138 si_gpioouten(adm
->sih
, pins
, 0, GPIO_DRV_PRIORITY
);
139 OSL_DELAY(EECK_EDGE_TIME
);
142 /* Advance clock(s) */
144 adm_adclk(adm_info_t
*adm
, int clocks
)
147 for (i
= 0; i
< clocks
; i
++) {
149 si_gpioout(adm
->sih
, adm
->eesk
, adm
->eesk
, GPIO_DRV_PRIORITY
);
150 OSL_DELAY(EECK_EDGE_TIME
);
152 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
153 OSL_DELAY(EECK_EDGE_TIME
);
157 /* Write a bit stream to the chip */
159 adm_write(adm_info_t
*adm
, int cs
, uint8
*buf
, uint bits
)
161 uint i
, len
= (bits
+ 7) / 8;
166 si_gpioout(adm
->sih
, adm
->eecs
, adm
->eecs
, GPIO_DRV_PRIORITY
);
168 si_gpioout(adm
->sih
, adm
->eecs
, 0, GPIO_DRV_PRIORITY
);
169 OSL_DELAY(EECK_EDGE_TIME
);
171 /* Byte assemble from MSB to LSB */
172 for (i
= 0; i
< len
; i
++) {
173 /* Bit bang from MSB to LSB */
174 for (mask
= 0x80; mask
&& bits
> 0; mask
>>= 1, bits
--) {
176 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
177 OSL_DELAY(EECK_EDGE_TIME
);
179 /* Output on rising edge */
181 si_gpioout(adm
->sih
, adm
->eedi
, adm
->eedi
, GPIO_DRV_PRIORITY
);
183 si_gpioout(adm
->sih
, adm
->eedi
, 0, GPIO_DRV_PRIORITY
);
184 OSL_DELAY(EEDI_SETUP_TIME
);
187 si_gpioout(adm
->sih
, adm
->eesk
, adm
->eesk
, GPIO_DRV_PRIORITY
);
188 OSL_DELAY(EECK_EDGE_TIME
);
193 si_gpioout(adm
->sih
, adm
->eesk
, 0, GPIO_DRV_PRIORITY
);
194 OSL_DELAY(EECK_EDGE_TIME
);
198 si_gpioout(adm
->sih
, adm
->eecs
, 0, GPIO_DRV_PRIORITY
);
201 /* Handy macros for writing fixed length values */
202 #define adm_write8(adm, cs, b) { uint8 val = (uint8) (b); adm_write(adm, cs, &val, sizeof(val)*8); }
203 #define adm_write16(adm, cs, w) { uint16 val = hton16(w); \
204 adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }
205 #define adm_write32(adm, cs, i) { uint32 val = hton32(i); \
206 adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }
208 /* Write chip configuration register */
209 /* Follow 93c66 timing and chip's min EEPROM timing requirement */
211 adm_wreg(adm_info_t
*adm
, uint8 addr
, uint16 val
)
213 /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
215 (0x05 << 5) | (addr
>> 3),
216 (addr
<< 5) | (uint8
)(val
>> 11),
221 ET_TRACE(("adm_wreg: addr %02x val %04x (%02X%02X%02X%02X)\n",
222 addr
, val
, bits
[0], bits
[1], bits
[2], bits
[3]));
224 /* Enable GPIO outputs with all pins to 0 */
225 adm_enout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
, 0);
227 /* Write cmd. Total 27 bits */
228 adm_write(adm
, 1, bits
, 27);
230 /* Extra clock(s) required per datasheet */
233 /* Disable GPIO outputs */
234 adm_disout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
);
237 /* Configure the chip based on nvram settings */
239 adm_config_vlan(adm_info_t
*adm
)
241 /* Port configuration */
243 uint8 addr
; /* port configuration register */
244 uint16 vlan
; /* vlan port mapping */
245 uint8 tagged
; /* output tagging */
246 uint8 cpu
; /* cpu port? 1 - yes, 0 - no */
247 uint16 pvid
; /* cpu port pvid */
254 #if defined(PMON) || defined(_CFE_)
255 {9, 1<<8, 0, 1, -1} /* no output tagging for pmon/cfe */
256 #else /* #if defined(PMON) || defined(CFE) */
257 {9, 1<<8, 1, 1, -1} /* output tagging for linux... */
258 #endif /* #if defined(PMON) || defined(CFE) */
260 /* Vlan ports bitmap */
262 uint8 addr
; /* vlan port map register */
283 /* Enable access to the switch */
286 /* vlan mode select register (0x11): vlan on, mac clone */
287 adm_wreg(adm
, 0x11, 0xff30);
289 /* vlan port group: port configuration, vlan port map */
290 /* VLAN ID is equal to vlan number, max 16 vlans */
291 for (vid
= 0; vid
< 16; vid
++) {
292 char port
[] = "XXXX", *next
, *ports
, *cur
;
293 char vlanports
[] = "vlanXXXXports";
298 /* no members if VLAN id is out of limitation */
299 if (vid
> VLAN_MAXVID
)
302 /* get nvram port settings */
303 sprintf(vlanports
, "vlan%dports", vid
);
304 ports
= getvar(adm
->vars
, vlanports
);
306 /* disable this vlan if not defined */
311 * port configuration register (0x01, 0x03, 0x05, 0x07, 0x08, 0x09):
312 * input/output tagging, pvid, auto mdix, auto negotiation, ...
313 * cpu port needs special handing to support pmon/cfe/linux...
315 for (cur
= ports
; cur
; cur
= next
) {
316 /* tokenize the port list */
319 next
= bcmstrstr(cur
, " ");
320 len
= next
? next
- cur
: strlen(cur
);
323 if (len
> sizeof(port
) - 1)
324 len
= sizeof(port
) - 1;
325 strncpy(port
, cur
, len
);
328 /* make sure port # is within the range */
329 port_num
= bcm_atoi(port
);
330 if (port_num
>= sizeof(port_cfg_tab
) / sizeof(port_cfg_tab
[0])) {
331 ET_ERROR(("port number %d is out of range\n", port_num
));
335 /* build vlan port map */
336 vlan_map
|= port_cfg_tab
[port_num
].vlan
;
338 /* cpu port needs special care */
339 if (port_cfg_tab
[port_num
].cpu
) {
340 /* cpu port's default VLAN is lan! */
341 if (strchr(port
, '*'))
342 port_cfg_tab
[port_num
].pvid
= vid
;
343 /* will be done later */
348 port_cfg
= 0x8000 | /* auto mdix */
349 (vid
<< 10) | /* pvid */
350 0x000f; /* full duplex, 100Mbps, auto neg, flow ctrl */
351 adm_wreg(adm
, port_cfg_tab
[port_num
].addr
, port_cfg
);
354 /* vlan port map register (0x13 - 0x22) */
355 adm_wreg(adm
, vlan_cfg_tab
[vid
].addr
, vlan_map
);
358 /* cpu port config: auto mdix, pvid, output tagging, ... */
359 for (i
= 0; i
< sizeof(port_cfg_tab
)/sizeof(port_cfg_tab
[0]); i
++) {
364 if (port_cfg_tab
[i
].cpu
== 0 || port_cfg_tab
[i
].pvid
== 0xffff)
368 tagged
= port_cfg_tab
[i
].tagged
? 1 : 0;
369 pvid
= port_cfg_tab
[i
].pvid
;
370 port_cfg
= 0x8000 | /* auto mdix */
371 (pvid
<< 10) | /* pvid */
372 (tagged
<< 4) | /* output tagging */
373 0x000f; /* full duplex, 100Mbps, auto neg, flow ctrl */
374 adm_wreg(adm
, port_cfg_tab
[i
].addr
, port_cfg
);
377 /* Disable access to the switch */
384 * Enable the chip with preset default configuration:
386 * TP Auto MDIX (EESK/GPIO = 1)
387 * Single Color LED (EEDI/GPIO = 0)
388 * EEPROM Disable (H/W pull down)
391 adm_enable_device(adm_info_t
*adm
)
396 /* Check nvram override existance */
397 if ((rc
= getgpiopin(adm
->vars
, "adm_rc", GPIO_PIN_NOTDEFINED
)) == GPIO_PIN_NOTDEFINED
)
401 /* Enable access to the switch */
404 * Reset sequence: RC high->low(100ms)->high(30ms)
406 * WAR: Certain boards don't have the correct power on
407 * reset logic therefore we must explicitly perform the
408 * sequece in software.
410 /* Keep RC high for at least 20ms */
411 adm_enout(adm
, rc
, rc
);
412 for (i
= 0; i
< 20; i
++)
414 /* Keep RC low for at least 100ms */
415 adm_enout(adm
, rc
, 0);
416 for (i
= 0; i
< 100; i
++)
418 /* Set default configuration */
419 adm_enout(adm
, adm
->eesk
| adm
->eedi
, adm
->eesk
);
420 /* Keep RC high for at least 30ms */
421 adm_enout(adm
, rc
, rc
);
422 for (i
= 0; i
< 30; i
++)
424 /* Leave RC high and disable GPIO outputs */
425 adm_disout(adm
, adm
->eecs
| adm
->eesk
| adm
->eedi
);
426 /* Disable access to the switch */