Broadcom SDK and wireless driver: another attempt to update to ver. 5.10.147.0
[tomato.git] / release / src-rt / et / sys / etc_adm.c
blob6f00fe93e51d09416a2cf0c112f0e91bf25adc43
1 /*
2 * ADMtek switch setup functions
4 * Copyright (C) 2009, Broadcom Corporation
5 * All Rights Reserved.
6 *
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 $
15 #include <typedefs.h>
16 #include <osl.h>
17 #include <bcmutils.h>
18 #include <siutils.h>
19 #include <bcmendian.h>
20 #include <bcmparams.h>
21 #include <etc_adm.h>
22 #include <et_dbg.h>
24 /* Private state per ADM switch */
25 struct adm_info_s {
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 */
38 adm_info_t *
39 adm_attach(si_t *sih, char *vars)
41 adm_info_t *adm;
42 int gpio;
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))));
47 return NULL;
49 bzero((char *) adm, sizeof(adm_info_t));
50 adm->sih = sih;
51 adm->vars = vars;
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"));
58 goto error;
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"));
66 goto error;
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"));
74 goto error;
76 adm->eedi = 1 << gpio;
78 return adm;
80 error:
81 adm_detach(adm);
82 return NULL;
85 /* Release private resource */
86 void
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 */
101 static void
102 adm_enable(adm_info_t *adm)
104 void *regs;
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);
111 ASSERT(regs);
114 /* Disable register access to the chip */
115 static void
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 */
123 static void
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 */
134 static void
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) */
143 static void
144 adm_adclk(adm_info_t *adm, int clocks)
146 int i;
147 for (i = 0; i < clocks; i ++) {
148 /* Clock high */
149 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
150 OSL_DELAY(EECK_EDGE_TIME);
151 /* Clock low */
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 */
158 static void
159 adm_write(adm_info_t *adm, int cs, uint8 *buf, uint bits)
161 uint i, len = (bits + 7) / 8;
162 uint8 mask;
164 /* CS high/low */
165 if (cs)
166 si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY);
167 else
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 --) {
175 /* Clock low */
176 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
177 OSL_DELAY(EECK_EDGE_TIME);
179 /* Output on rising edge */
180 if (mask & buf[i])
181 si_gpioout(adm->sih, adm->eedi, adm->eedi, GPIO_DRV_PRIORITY);
182 else
183 si_gpioout(adm->sih, adm->eedi, 0, GPIO_DRV_PRIORITY);
184 OSL_DELAY(EEDI_SETUP_TIME);
186 /* Clock high */
187 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
188 OSL_DELAY(EECK_EDGE_TIME);
192 /* Clock low */
193 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
194 OSL_DELAY(EECK_EDGE_TIME);
196 /* CS low */
197 if (cs)
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 */
210 static void
211 adm_wreg(adm_info_t *adm, uint8 addr, uint16 val)
213 /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
214 uint8 bits[4] = {
215 (0x05 << 5) | (addr >> 3),
216 (addr << 5) | (uint8)(val >> 11),
217 (uint8)(val >> 3),
218 (uint8)(val << 5)
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 */
231 adm_adclk(adm, 2);
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 */
242 struct {
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 */
248 } port_cfg_tab[] = {
249 {1, 1<<0, 0, 0, -1},
250 {3, 1<<2, 0, 0, -1},
251 {5, 1<<4, 0, 0, -1},
252 {7, 1<<6, 0, 0, -1},
253 {8, 1<<7, 0, 0, -1},
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 */
261 struct {
262 uint8 addr; /* vlan port map register */
263 } vlan_cfg_tab[] = {
264 {0x13},
265 {0x14},
266 {0x15},
267 {0x16},
268 {0x17},
269 {0x18},
270 {0x19},
271 {0x1a},
272 {0x1b},
273 {0x1c},
274 {0x1d},
275 {0x1e},
276 {0x1f},
277 {0x20},
278 {0x21},
279 {0x22}
281 uint16 vid, i;
283 /* Enable access to the switch */
284 adm_enable(adm);
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";
294 uint16 vlan_map = 0;
295 int port_num, len;
296 uint16 port_cfg;
298 /* no members if VLAN id is out of limitation */
299 if (vid > VLAN_MAXVID)
300 goto vlan_setup;
302 /* get nvram port settings */
303 sprintf(vlanports, "vlan%dports", vid);
304 ports = getvar(adm->vars, vlanports);
306 /* disable this vlan if not defined */
307 if (!ports)
308 goto vlan_setup;
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 */
317 while (*cur == ' ')
318 cur ++;
319 next = bcmstrstr(cur, " ");
320 len = next ? next - cur : strlen(cur);
321 if (!len)
322 break;
323 if (len > sizeof(port) - 1)
324 len = sizeof(port) - 1;
325 strncpy(port, cur, len);
326 port[len] = 0;
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));
332 continue;
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 */
344 continue;
347 /* configure port */
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);
353 vlan_setup:
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 ++) {
360 uint16 tagged, pvid;
361 uint16 port_cfg;
363 /* cpu port only */
364 if (port_cfg_tab[i].cpu == 0 || port_cfg_tab[i].pvid == 0xffff)
365 continue;
367 /* configure port */
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 */
378 adm_disable(adm);
380 return 0;
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)
393 uint32 rc;
394 int i;
396 /* Check nvram override existance */
397 if ((rc = getgpiopin(adm->vars, "adm_rc", GPIO_PIN_NOTDEFINED)) == GPIO_PIN_NOTDEFINED)
398 return 0;
399 rc = 1 << rc;
401 /* Enable access to the switch */
402 adm_enable(adm);
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 ++)
413 OSL_DELAY(1000);
414 /* Keep RC low for at least 100ms */
415 adm_enout(adm, rc, 0);
416 for (i = 0; i < 100; i++)
417 OSL_DELAY(1000);
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++)
423 OSL_DELAY(1000);
424 /* Leave RC high and disable GPIO outputs */
425 adm_disout(adm, adm->eecs | adm->eesk | adm->eedi);
426 /* Disable access to the switch */
427 adm_disable(adm);
428 return 0;