usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src-rt / et / sys / etc_adm.c
blob74a35b452928206d3e4edccabaf6d6aa680dd987
1 /*
2 * ADMtek switch setup functions
4 * Copyright (C) 2010, 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 $
14 #include <typedefs.h>
15 #include <osl.h>
16 #include <bcmutils.h>
17 #include <siutils.h>
18 #include <bcmendian.h>
19 #include <bcmparams.h>
20 #include <etc_adm.h>
21 #include <et_dbg.h>
23 /* Private state per ADM switch */
24 struct adm_info_s {
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 */
37 adm_info_t *
38 adm_attach(si_t *sih, char *vars)
40 adm_info_t *adm;
41 int gpio;
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))));
46 return NULL;
48 bzero((char *) adm, sizeof(adm_info_t));
49 adm->sih = sih;
50 adm->vars = vars;
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"));
57 goto error;
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"));
65 goto error;
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"));
73 goto error;
75 adm->eedi = 1 << gpio;
77 return adm;
79 error:
80 adm_detach(adm);
81 return NULL;
84 /* Release private resource */
85 void
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 */
100 static void
101 adm_enable(adm_info_t *adm)
103 void *regs;
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);
110 ASSERT(regs);
113 /* Disable register access to the chip */
114 static void
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 */
122 static void
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 */
133 static void
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) */
142 static void
143 adm_adclk(adm_info_t *adm, int clocks)
145 int i;
146 for (i = 0; i < clocks; i ++) {
147 /* Clock high */
148 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
149 OSL_DELAY(EECK_EDGE_TIME);
150 /* Clock low */
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 */
157 static void
158 adm_write(adm_info_t *adm, int cs, uint8 *buf, uint bits)
160 uint i, len = (bits + 7) / 8;
161 uint8 mask;
163 /* CS high/low */
164 if (cs)
165 si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY);
166 else
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 --) {
174 /* Clock low */
175 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
176 OSL_DELAY(EECK_EDGE_TIME);
178 /* Output on rising edge */
179 if (mask & buf[i])
180 si_gpioout(adm->sih, adm->eedi, adm->eedi, GPIO_DRV_PRIORITY);
181 else
182 si_gpioout(adm->sih, adm->eedi, 0, GPIO_DRV_PRIORITY);
183 OSL_DELAY(EEDI_SETUP_TIME);
185 /* Clock high */
186 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
187 OSL_DELAY(EECK_EDGE_TIME);
191 /* Clock low */
192 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
193 OSL_DELAY(EECK_EDGE_TIME);
195 /* CS low */
196 if (cs)
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 */
209 static void
210 adm_wreg(adm_info_t *adm, uint8 addr, uint16 val)
212 /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
213 uint8 bits[4] = {
214 (0x05 << 5) | (addr >> 3),
215 (addr << 5) | (uint8)(val >> 11),
216 (uint8)(val >> 3),
217 (uint8)(val << 5)
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 */
230 adm_adclk(adm, 2);
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 */
241 struct {
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 */
247 } port_cfg_tab[] = {
248 {1, 1<<0, 0, 0, -1},
249 {3, 1<<2, 0, 0, -1},
250 {5, 1<<4, 0, 0, -1},
251 {7, 1<<6, 0, 0, -1},
252 {8, 1<<7, 0, 0, -1},
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 */
260 struct {
261 uint8 addr; /* vlan port map register */
262 } vlan_cfg_tab[] = {
263 {0x13},
264 {0x14},
265 {0x15},
266 {0x16},
267 {0x17},
268 {0x18},
269 {0x19},
270 {0x1a},
271 {0x1b},
272 {0x1c},
273 {0x1d},
274 {0x1e},
275 {0x1f},
276 {0x20},
277 {0x21},
278 {0x22}
280 uint16 vid, i;
282 /* Enable access to the switch */
283 adm_enable(adm);
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";
293 uint16 vlan_map = 0;
294 int port_num, len;
295 uint16 port_cfg;
297 /* no members if VLAN id is out of limitation */
298 if (vid > VLAN_MAXVID)
299 goto vlan_setup;
301 /* get nvram port settings */
302 sprintf(vlanports, "vlan%dports", vid);
303 ports = getvar(adm->vars, vlanports);
305 /* disable this vlan if not defined */
306 if (!ports)
307 goto vlan_setup;
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 */
316 while (*cur == ' ')
317 cur ++;
318 next = bcmstrstr(cur, " ");
319 len = next ? next - cur : strlen(cur);
320 if (!len)
321 break;
322 if (len > sizeof(port) - 1)
323 len = sizeof(port) - 1;
324 strncpy(port, cur, len);
325 port[len] = 0;
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));
331 continue;
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 */
343 continue;
346 /* configure port */
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);
352 vlan_setup:
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 ++) {
359 uint16 tagged, pvid;
360 uint16 port_cfg;
362 /* cpu port only */
363 if (port_cfg_tab[i].cpu == 0 || port_cfg_tab[i].pvid == 0xffff)
364 continue;
366 /* configure port */
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 */
377 adm_disable(adm);
379 return 0;
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)
392 uint32 rc;
393 int i;
395 /* Check nvram override existance */
396 if ((rc = getgpiopin(adm->vars, "adm_rc", GPIO_PIN_NOTDEFINED)) == GPIO_PIN_NOTDEFINED)
397 return 0;
398 rc = 1 << rc;
400 /* Enable access to the switch */
401 adm_enable(adm);
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 ++)
412 OSL_DELAY(1000);
413 /* Keep RC low for at least 100ms */
414 adm_enout(adm, rc, 0);
415 for (i = 0; i < 100; i++)
416 OSL_DELAY(1000);
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++)
422 OSL_DELAY(1000);
423 /* Leave RC high and disable GPIO outputs */
424 adm_disout(adm, adm->eecs | adm->eesk | adm->eedi);
425 /* Disable access to the switch */
426 adm_disable(adm);
427 return 0;
430 #ifdef BCMDBG
431 /* Read a bit stream from the chip */
432 static void
433 adm_read(adm_info_t *adm, int cs, uint32 pin, uint8 *buf, uint bits)
435 uint i, len = (bits + 7) / 8;
437 /* CS high/low */
438 if (cs)
439 si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY);
440 else
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 --) {
449 /* Clock high */
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)
455 byte |= mask;
457 /* Clock low */
458 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
459 OSL_DELAY(EECK_EDGE_TIME);
461 buf[i] = byte;
464 /* CS low */
465 if (cs)
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 */
479 static uint32
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;
486 uint32 data;
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 */
501 adm_adclk(adm, 1);
503 /* Register value: 32 bits */
504 adm_read32(adm, 0, adm->eedi, &data);
506 /* Idle: at least 1 extra clock */
507 adm_adclk(adm, 2);
509 /* Disable GPIO outputs */
510 adm_disout(adm, adm->eecs | adm->eesk);
512 return data;
515 char*
516 adm_dump_regs(adm_info_t *adm, char *buf)
518 uint32 v;
519 int i;
521 /* enable access to the switch */
522 adm_enable(adm);
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)));
530 if ((i % 8) == 7)
531 buf += sprintf(buf, "\n");
533 buf += sprintf(buf, "\n");
535 /* disable access to the switch */
536 adm_disable(adm);
538 return (buf);
540 #endif /* BCMDBG */