1/* 2 * ADMtek switch setup functions 3 * 4 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: etc_adm.c 286404 2011-09-27 19:29:08Z $ 19 */ 20 21#include <et_cfg.h> 22#include <typedefs.h> 23#include <osl.h> 24#include <bcmutils.h> 25#include <siutils.h> 26#include <bcmendian.h> 27#include <bcmparams.h> 28#include <etc_adm.h> 29#include <et_dbg.h> 30 31/* Private state per ADM switch */ 32struct adm_info_s { 33 si_t *sih; /* SiliconBackplane handle */ 34 void *vars; /* variables handle */ 35 uint coreidx; /* Current core index */ 36 uint32 eecs, eesk, eedi; /* GPIO mapping */ 37}; 38 39/* Minimum timing constants */ 40#define EECK_EDGE_TIME 3 /* 3us - max(adm 2.5us, 93c 1us) */ 41#define EEDI_SETUP_TIME 1 /* 1us - max(adm 10ns, 93c 400ns) */ 42#define EECS_SETUP_TIME 1 /* 1us - max(adm no, 93c 200ns) */ 43 44/* Allocate private resource */ 45adm_info_t * 46adm_attach(si_t *sih, char *vars) 47{ 48 adm_info_t *adm; 49 int gpio; 50 51 /* Allocate private data */ 52 if (!(adm = MALLOC(si_osh(sih), sizeof(adm_info_t)))) { 53 ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih)))); 54 return NULL; 55 } 56 bzero((char *) adm, sizeof(adm_info_t)); 57 adm->sih = sih; 58 adm->vars = vars; 59 60 /* Init GPIO mapping. Default GPIO: 2, 3, 4 */ 61 gpio = getgpiopin(vars, "adm_eecs", 2); 62 ET_ERROR(("adm_attach: got %d as adm_eecs", gpio)); 63 if (gpio == GPIO_PIN_NOTDEFINED) { 64 ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use")); 65 goto error; 66 } 67 adm->eecs = 1 << gpio; 68 69 gpio = getgpiopin(vars, "adm_eesk", 3); 70 ET_ERROR(("adm_attach: got %d as adm_eesk", gpio)); 71 if (gpio == GPIO_PIN_NOTDEFINED) { 72 ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use")); 73 goto error; 74 } 75 adm->eesk = 1 << gpio; 76 77 gpio = getgpiopin(vars, "adm_eedi", 4); 78 ET_ERROR(("adm_attach: got %d as adm_eedi", gpio)); 79 if (gpio == GPIO_PIN_NOTDEFINED) { 80 ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use")); 81 goto error; 82 } 83 adm->eedi = 1 << gpio; 84 85 return adm; 86 87error: 88 adm_detach(adm); 89 return NULL; 90} 91 92/* Release private resource */ 93void 94adm_detach(adm_info_t *adm) 95{ 96 /* Free private data */ 97 MFREE(si_osh(adm->sih), adm, sizeof(adm_info_t)); 98} 99 100/* 101* The following local functions provide chip access control. The 102* general rules in writing these supporting routines are: 103* 104* 1. EECS should be kept low after each routine. 105* 2. EESK should be kept low after each routine. 106*/ 107/* Enable register access to the chip */ 108static void 109adm_enable(adm_info_t *adm) 110{ 111 void *regs; 112 113 /* Save current core index */ 114 adm->coreidx = si_coreidx(adm->sih); 115 116 /* Switch to GPIO core for faster access */ 117 regs = si_gpiosetcore(adm->sih); 118 ASSERT(regs); 119} 120 121/* Disable register access to the chip */ 122static void 123adm_disable(adm_info_t *adm) 124{ 125 /* Switch back to original core */ 126 si_setcoreidx(adm->sih, adm->coreidx); 127} 128 129/* Enable outputs with specified value to the chip */ 130static void 131adm_enout(adm_info_t *adm, uint32 pins, uint val) 132{ 133 /* Prepare GPIO output value */ 134 si_gpioout(adm->sih, pins, val, GPIO_DRV_PRIORITY); 135 /* Enable GPIO outputs */ 136 si_gpioouten(adm->sih, pins, pins, GPIO_DRV_PRIORITY); 137 OSL_DELAY(EECK_EDGE_TIME); 138} 139 140/* Disable outputs to the chip */ 141static void 142adm_disout(adm_info_t *adm, uint32 pins) 143{ 144 /* Disable GPIO outputs */ 145 si_gpioouten(adm->sih, pins, 0, GPIO_DRV_PRIORITY); 146 OSL_DELAY(EECK_EDGE_TIME); 147} 148 149/* Advance clock(s) */ 150static void 151adm_adclk(adm_info_t *adm, int clocks) 152{ 153 int i; 154 for (i = 0; i < clocks; i ++) { 155 /* Clock high */ 156 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY); 157 OSL_DELAY(EECK_EDGE_TIME); 158 /* Clock low */ 159 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY); 160 OSL_DELAY(EECK_EDGE_TIME); 161 } 162} 163 164/* Write a bit stream to the chip */ 165static void 166adm_write(adm_info_t *adm, int cs, uint8 *buf, uint bits) 167{ 168 uint i, len = (bits + 7) / 8; 169 uint8 mask; 170 171 /* CS high/low */ 172 if (cs) 173 si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY); 174 else 175 si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY); 176 OSL_DELAY(EECK_EDGE_TIME); 177 178 /* Byte assemble from MSB to LSB */ 179 for (i = 0; i < len; i++) { 180 /* Bit bang from MSB to LSB */ 181 for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) { 182 /* Clock low */ 183 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY); 184 OSL_DELAY(EECK_EDGE_TIME); 185 186 /* Output on rising edge */ 187 if (mask & buf[i]) 188 si_gpioout(adm->sih, adm->eedi, adm->eedi, GPIO_DRV_PRIORITY); 189 else 190 si_gpioout(adm->sih, adm->eedi, 0, GPIO_DRV_PRIORITY); 191 OSL_DELAY(EEDI_SETUP_TIME); 192 193 /* Clock high */ 194 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY); 195 OSL_DELAY(EECK_EDGE_TIME); 196 } 197 } 198 199 /* Clock low */ 200 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY); 201 OSL_DELAY(EECK_EDGE_TIME); 202 203 /* CS low */ 204 if (cs) 205 si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY); 206} 207 208/* Handy macros for writing fixed length values */ 209#define adm_write8(adm, cs, b) { uint8 val = (uint8) (b); adm_write(adm, cs, &val, sizeof(val)*8); } 210#define adm_write16(adm, cs, w) { uint16 val = hton16(w); \ 211 adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); } 212#define adm_write32(adm, cs, i) { uint32 val = hton32(i); \ 213 adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); } 214 215/* Write chip configuration register */ 216/* Follow 93c66 timing and chip's min EEPROM timing requirement */ 217static void 218adm_wreg(adm_info_t *adm, uint8 addr, uint16 val) 219{ 220 /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */ 221 uint8 bits[4] = { 222 (0x05 << 5) | (addr >> 3), 223 (addr << 5) | (uint8)(val >> 11), 224 (uint8)(val >> 3), 225 (uint8)(val << 5) 226 }; 227 228 ET_TRACE(("adm_wreg: addr %02x val %04x (%02X%02X%02X%02X)\n", 229 addr, val, bits[0], bits[1], bits[2], bits[3])); 230 231 /* Enable GPIO outputs with all pins to 0 */ 232 adm_enout(adm, adm->eecs | adm->eesk | adm->eedi, 0); 233 234 /* Write cmd. Total 27 bits */ 235 adm_write(adm, 1, bits, 27); 236 237 /* Extra clock(s) required per datasheet */ 238 adm_adclk(adm, 2); 239 240 /* Disable GPIO outputs */ 241 adm_disout(adm, adm->eecs | adm->eesk | adm->eedi); 242} 243 244/* Configure the chip based on nvram settings */ 245int 246adm_config_vlan(adm_info_t *adm) 247{ 248 /* Port configuration */ 249 struct { 250 uint8 addr; /* port configuration register */ 251 uint16 vlan; /* vlan port mapping */ 252 uint8 tagged; /* output tagging */ 253 uint8 cpu; /* cpu port? 1 - yes, 0 - no */ 254 uint16 pvid; /* cpu port pvid */ 255 } port_cfg_tab[] = { 256 {1, 1<<0, 0, 0, -1}, 257 {3, 1<<2, 0, 0, -1}, 258 {5, 1<<4, 0, 0, -1}, 259 {7, 1<<6, 0, 0, -1}, 260 {8, 1<<7, 0, 0, -1}, 261#if defined(PMON) || defined(_CFE_) 262 {9, 1<<8, 0, 1, -1} /* no output tagging for pmon/cfe */ 263#else /* #if defined(PMON) || defined(CFE) */ 264 {9, 1<<8, 1, 1, -1} /* output tagging for linux... */ 265#endif /* #if defined(PMON) || defined(CFE) */ 266 }; 267 /* Vlan ports bitmap */ 268 struct { 269 uint8 addr; /* vlan port map register */ 270 } vlan_cfg_tab[] = { 271 {0x13}, 272 {0x14}, 273 {0x15}, 274 {0x16}, 275 {0x17}, 276 {0x18}, 277 {0x19}, 278 {0x1a}, 279 {0x1b}, 280 {0x1c}, 281 {0x1d}, 282 {0x1e}, 283 {0x1f}, 284 {0x20}, 285 {0x21}, 286 {0x22} 287 }; 288 uint16 vid, i; 289 290 /* Enable access to the switch */ 291 adm_enable(adm); 292 293 /* vlan mode select register (0x11): vlan on, mac clone */ 294 adm_wreg(adm, 0x11, 0xff30); 295 296 /* vlan port group: port configuration, vlan port map */ 297 /* VLAN ID is equal to vlan number, max 16 vlans */ 298 for (vid = 0; vid < 16; vid ++) { 299 char port[] = "XXXX", *ports, *next, *cur; 300 char vlanports[] = "vlanXXXXports"; 301 uint16 vlan_map = 0; 302 int port_num, len; 303 uint16 port_cfg; 304 305 /* no members if VLAN id is out of limitation */ 306 if (vid > VLAN_MAXVID) 307 goto vlan_setup; 308 309 /* get nvram port settings */ 310 sprintf(vlanports, "vlan%dports", vid); 311 ports = getvar(adm->vars, vlanports); 312 313 /* disable this vlan if not defined */ 314 if (!ports) 315 goto vlan_setup; 316 317 /* 318 * port configuration register (0x01, 0x03, 0x05, 0x07, 0x08, 0x09): 319 * input/output tagging, pvid, auto mdix, auto negotiation, ... 320 * cpu port needs special handing to support pmon/cfe/linux... 321 */ 322 for (cur = ports; cur; cur = next) { 323 /* tokenize the port list */ 324 while (*cur == ' ') 325 cur ++; 326 next = bcmstrstr(cur, " "); 327 len = next ? next - cur : strlen(cur); 328 if (!len) 329 break; 330 if (len > sizeof(port) - 1) 331 len = sizeof(port) - 1; 332 strncpy(port, cur, len); 333 port[len] = 0; 334 335 /* make sure port # is within the range */ 336 port_num = bcm_atoi(port); 337 if (port_num >= sizeof(port_cfg_tab) / sizeof(port_cfg_tab[0])) { 338 ET_ERROR(("port number %d is out of range\n", port_num)); 339 continue; 340 } 341 342 /* build vlan port map */ 343 vlan_map |= port_cfg_tab[port_num].vlan; 344 345 /* cpu port needs special care */ 346 if (port_cfg_tab[port_num].cpu) { 347 /* cpu port's default VLAN is lan! */ 348 if (strchr(port, '*')) 349 port_cfg_tab[port_num].pvid = vid; 350 /* will be done later */ 351 continue; 352 } 353 354 /* configure port */ 355 port_cfg = 0x8000 | /* auto mdix */ 356 (vid << 10) | /* pvid */ 357 0x000f; /* full duplex, 100Mbps, auto neg, flow ctrl */ 358 adm_wreg(adm, port_cfg_tab[port_num].addr, port_cfg); 359 } 360vlan_setup: 361 /* vlan port map register (0x13 - 0x22) */ 362 adm_wreg(adm, vlan_cfg_tab[vid].addr, vlan_map); 363 } 364 365 /* cpu port config: auto mdix, pvid, output tagging, ... */ 366 for (i = 0; i < sizeof(port_cfg_tab)/sizeof(port_cfg_tab[0]); i ++) { 367 uint16 tagged, pvid; 368 uint16 port_cfg; 369 370 /* cpu port only */ 371 if (port_cfg_tab[i].cpu == 0 || port_cfg_tab[i].pvid == 0xffff) 372 continue; 373 374 /* configure port */ 375 tagged = port_cfg_tab[i].tagged ? 1 : 0; 376 pvid = port_cfg_tab[i].pvid; 377 port_cfg = 0x8000 | /* auto mdix */ 378 (pvid << 10) | /* pvid */ 379 (tagged << 4) | /* output tagging */ 380 0x000f; /* full duplex, 100Mbps, auto neg, flow ctrl */ 381 adm_wreg(adm, port_cfg_tab[i].addr, port_cfg); 382 } 383 384 /* Disable access to the switch */ 385 adm_disable(adm); 386 387 return 0; 388} 389 390/* 391* Enable the chip with preset default configuration: 392* 393* TP Auto MDIX (EESK/GPIO = 1) 394* Single Color LED (EEDI/GPIO = 0) 395* EEPROM Disable (H/W pull down) 396*/ 397int 398adm_enable_device(adm_info_t *adm) 399{ 400 uint32 rc; 401 int i; 402 403 /* Check nvram override existance */ 404 if ((rc = getgpiopin(adm->vars, "adm_rc", GPIO_PIN_NOTDEFINED)) == GPIO_PIN_NOTDEFINED) 405 return 0; 406 rc = 1 << rc; 407 408 /* Enable access to the switch */ 409 adm_enable(adm); 410 /* 411 * Reset sequence: RC high->low(100ms)->high(30ms) 412 * 413 * WAR: Certain boards don't have the correct power on 414 * reset logic therefore we must explicitly perform the 415 * sequece in software. 416 */ 417 /* Keep RC high for at least 20ms */ 418 adm_enout(adm, rc, rc); 419 for (i = 0; i < 20; i ++) 420 OSL_DELAY(1000); 421 /* Keep RC low for at least 100ms */ 422 adm_enout(adm, rc, 0); 423 for (i = 0; i < 100; i++) 424 OSL_DELAY(1000); 425 /* Set default configuration */ 426 adm_enout(adm, adm->eesk | adm->eedi, adm->eesk); 427 /* Keep RC high for at least 30ms */ 428 adm_enout(adm, rc, rc); 429 for (i = 0; i < 30; i++) 430 OSL_DELAY(1000); 431 /* Leave RC high and disable GPIO outputs */ 432 adm_disout(adm, adm->eecs | adm->eesk | adm->eedi); 433 /* Disable access to the switch */ 434 adm_disable(adm); 435 return 0; 436} 437 438#ifdef BCMDBG 439/* Read a bit stream from the chip */ 440static void 441adm_read(adm_info_t *adm, int cs, uint32 pin, uint8 *buf, uint bits) 442{ 443 uint i, len = (bits + 7) / 8; 444 445 /* CS high/low */ 446 if (cs) 447 si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY); 448 else 449 si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY); 450 OSL_DELAY(EECK_EDGE_TIME); 451 452 /* Byte assemble from MSB to LSB */ 453 for (i = 0; i < len; i ++) { 454 uint8 mask, byte = 0; 455 /* Bit bang from MSB to LSB */ 456 for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) { 457 /* Clock high */ 458 si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY); 459 OSL_DELAY(EECK_EDGE_TIME); 460 461 /* Sample on rising edge */ 462 if (si_gpioin(adm->sih) & pin) 463 byte |= mask; 464 465 /* Clock low */ 466 si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY); 467 OSL_DELAY(EECK_EDGE_TIME); 468 } 469 buf[i] = byte; 470 } 471 472 /* CS low */ 473 if (cs) 474 si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY); 475} 476 477/* Handy macros for reading fixed length values */ 478#define adm_read8(adm, cs, pin, b) { uint8 val; \ 479 adm_read(adm, cs, pin, &val, sizeof(val) * 8); *(b) = val; } 480#define adm_read16(adm, cs, pin, w) { uint16 val; \ 481 adm_read(adm, cs, pin, (uint8 *)&val, sizeof(val) * 8); *(w) = ntoh16(val); } 482#define adm_read32(adm, cs, pin, i) { uint32 val; \ 483 adm_read(adm, cs, pin, (uint8 *)&val, sizeof(val) * 8); *(i) = ntoh32(val); } 484 485/* Read counter/config register. table 0 - config registers, 1 - internal counters */ 486/* Follow chip's SMI timing */ 487static uint32 488adm_rreg(adm_info_t *adm, int addr, int table) 489{ 490 /* Command: preamble(11) + start(01) + opcode(10) + table(b) + 491 * device(00) + register(bbbbbbb) 492 */ 493 uint16 cmd = (3 << 14) | (1 << 12) | (2 << 10) | (table << 9) | (0 << 7) | addr; 494 uint32 data; 495 496 /* Enable GPIO outputs */ 497 adm_enout(adm, adm->eecs | adm->eesk | adm->eedi, 0); 498 499 /* Preamble: at lease 32 bit 1s */ 500 adm_write32(adm, 0, 0xffffffff); 501 502 /* Command: 2 extra preamble bits plus 14 command bits */ 503 adm_write16(adm, 0, cmd); 504 505 /* Z EEDI: the switch will drive it */ 506 adm_disout(adm, adm->eedi); 507 508 /* Turn around: 1 bit */ 509 adm_adclk(adm, 1); 510 511 /* Register value: 32 bits */ 512 adm_read32(adm, 0, adm->eedi, &data); 513 514 /* Idle: at least 1 extra clock */ 515 adm_adclk(adm, 2); 516 517 /* Disable GPIO outputs */ 518 adm_disout(adm, adm->eecs | adm->eesk); 519 520 return data; 521} 522 523char* 524adm_dump_regs(adm_info_t *adm, char *buf) 525{ 526 uint32 v; 527 int i; 528 529 /* enable access to the switch */ 530 adm_enable(adm); 531 532 /* dump admtek switch registers */ 533 buf += sprintf(buf, "admtek:\n"); 534 for (i = 0; i <= 0x2d; i++) { 535 v = adm_rreg(adm, i, 0); 536 buf += sprintf(buf, "%04x ", 537 ((i % 2) ? ((v >> 16) & 0xffff) : (v & 0xffff))); 538 if ((i % 8) == 7) 539 buf += sprintf(buf, "\n"); 540 } 541 buf += sprintf(buf, "\n"); 542 543 /* disable access to the switch */ 544 adm_disable(adm); 545 546 return (buf); 547} 548#endif /* BCMDBG */ 549