Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

orinoco: Extend hermes_dld routines for Agere firmware

Add programming initialisation and termination functions.
Add checks to avoid overrunning the firmware image or PDA areas.
Extra algorithm to program PDA values using defaults where necessary.

Signed-off-by: David Kilroy <kilroyd@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

David Kilroy and committed by
John W. Linville
8f5ae73c e2334180

+290
+285
drivers/net/wireless/hermes_dld.c
··· 70 70 #define HERMES_AUX_PW1 0xDC23 71 71 #define HERMES_AUX_PW2 0xBA45 72 72 73 + /* HERMES_CMD_DOWNLD */ 74 + #define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) 75 + #define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) 76 + #define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) 77 + #define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) 78 + 73 79 /* End markers used in dblocks */ 74 80 #define PDI_END 0x00000000 /* End of PDA */ 75 81 #define BLOCK_END 0xFFFFFFFF /* Last image block */ ··· 253 247 return NULL; 254 248 } 255 249 250 + /* Scan production data items for a particular entry */ 251 + static struct pdi * 252 + hermes_find_pdi(struct pdi *first_pdi, u32 record_id) 253 + { 254 + struct pdi *pdi = first_pdi; 255 + 256 + while (pdi_id(pdi) != PDI_END) { 257 + 258 + /* If the record ID matches, we are done */ 259 + if (pdi_id(pdi) == record_id) 260 + return pdi; 261 + 262 + pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; 263 + } 264 + return NULL; 265 + } 266 + 256 267 /* Process one Plug Data Item - find corresponding PDR and plug it */ 257 268 static int 258 269 hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) ··· 313 290 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); 314 291 if (ret) 315 292 return ret; 293 + } else { 294 + /* wl_lkm does not include PDA size in the PDA area. 295 + * We will pad the information into pda, so other routines 296 + * don't have to be modified */ 297 + pda[0] = cpu_to_le16(pda_len - 2); 298 + /* Includes CFG_PROD_DATA but not itself */ 299 + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ 300 + data_len = pda_len - 4; 301 + data = pda + 2; 316 302 } 317 303 318 304 /* Open auxiliary port */ ··· 402 370 403 371 /*** Hermes programming ***/ 404 372 373 + /* About to start programming data (Hermes I) 374 + * offset is the entry point 375 + * 376 + * Spectrum_cs' Symbol fw does not require this 377 + * wl_lkm Agere fw does 378 + * Don't know about intersil 379 + */ 380 + int hermesi_program_init(hermes_t *hw, u32 offset) 381 + { 382 + int err; 383 + 384 + /* Disable interrupts?*/ 385 + /*hw->inten = 0x0;*/ 386 + /*hermes_write_regn(hw, INTEN, 0);*/ 387 + /*hermes_set_irqmask(hw, 0);*/ 388 + 389 + /* Acknowledge any outstanding command */ 390 + hermes_write_regn(hw, EVACK, 0xFFFF); 391 + 392 + /* Using doicmd_wait rather than docmd_wait */ 393 + err = hermes_doicmd_wait(hw, 394 + 0x0100 | HERMES_CMD_INIT, 395 + 0, 0, 0, NULL); 396 + if (err) 397 + return err; 398 + 399 + err = hermes_doicmd_wait(hw, 400 + 0x0000 | HERMES_CMD_INIT, 401 + 0, 0, 0, NULL); 402 + if (err) 403 + return err; 404 + 405 + err = hermes_aux_control(hw, 1); 406 + printk(KERN_DEBUG PFX "AUX enable returned %d\n", err); 407 + 408 + if (err) 409 + return err; 410 + 411 + printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset); 412 + err = hermes_doicmd_wait(hw, 413 + HERMES_PROGRAM_ENABLE_VOLATILE, 414 + offset & 0xFFFFu, 415 + offset >> 16, 416 + 0, 417 + NULL); 418 + printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n", 419 + err); 420 + 421 + return err; 422 + } 423 + EXPORT_SYMBOL(hermesi_program_init); 424 + 425 + /* Done programming data (Hermes I) 426 + * 427 + * Spectrum_cs' Symbol fw does not require this 428 + * wl_lkm Agere fw does 429 + * Don't know about intersil 430 + */ 431 + int hermesi_program_end(hermes_t *hw) 432 + { 433 + struct hermes_response resp; 434 + int rc = 0; 435 + int err; 436 + 437 + rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); 438 + 439 + printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, " 440 + "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", 441 + rc, resp.resp0, resp.resp1, resp.resp2); 442 + 443 + if ((rc == 0) && 444 + ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) 445 + rc = -EIO; 446 + 447 + err = hermes_aux_control(hw, 0); 448 + printk(KERN_DEBUG PFX "AUX disable returned %d\n", err); 449 + 450 + /* Acknowledge any outstanding command */ 451 + hermes_write_regn(hw, EVACK, 0xFFFF); 452 + 453 + /* Reinitialise, ignoring return */ 454 + (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT, 455 + 0, 0, 0, NULL); 456 + 457 + return rc ? rc : err; 458 + } 459 + EXPORT_SYMBOL(hermesi_program_end); 460 + 405 461 /* Program the data blocks */ 406 462 int hermes_program(hermes_t *hw, const char *first_block, const char *end) 407 463 { ··· 563 443 564 444 module_init(init_hermes_dld); 565 445 module_exit(exit_hermes_dld); 446 + 447 + /*** Default plugging data for Hermes I ***/ 448 + /* Values from wl_lkm_718/hcf/dhf.c */ 449 + 450 + #define DEFINE_DEFAULT_PDR(pid, length, data) \ 451 + static const struct { \ 452 + __le16 len; \ 453 + __le16 id; \ 454 + u8 val[length]; \ 455 + } __attribute__ ((packed)) default_pdr_data_##pid = { \ 456 + __constant_cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ 457 + sizeof(__le16)) - 1), \ 458 + __constant_cpu_to_le16(pid), \ 459 + data \ 460 + } 461 + 462 + #define DEFAULT_PDR(pid) default_pdr_data_##pid 463 + 464 + /* HWIF Compatiblity */ 465 + DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); 466 + 467 + /* PPPPSign */ 468 + DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); 469 + 470 + /* PPPPProf */ 471 + DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); 472 + 473 + /* Antenna diversity */ 474 + DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); 475 + 476 + /* Modem VCO band Set-up */ 477 + DEFINE_DEFAULT_PDR(0x0160, 28, 478 + "\x00\x00\x00\x00\x00\x00\x00\x00" 479 + "\x00\x00\x00\x00\x00\x00\x00\x00" 480 + "\x00\x00\x00\x00\x00\x00\x00\x00" 481 + "\x00\x00\x00\x00"); 482 + 483 + /* Modem Rx Gain Table Values */ 484 + DEFINE_DEFAULT_PDR(0x0161, 256, 485 + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 486 + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 487 + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 488 + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 489 + "\x3F\x01\x3E\01\x3E\x01\x3D\x01" 490 + "\x3D\x01\x3C\01\x3C\x01\x3B\x01" 491 + "\x3B\x01\x3A\01\x3A\x01\x39\x01" 492 + "\x39\x01\x38\01\x38\x01\x37\x01" 493 + "\x37\x01\x36\01\x36\x01\x35\x01" 494 + "\x35\x01\x34\01\x34\x01\x33\x01" 495 + "\x33\x01\x32\x01\x32\x01\x31\x01" 496 + "\x31\x01\x30\x01\x30\x01\x7B\x01" 497 + "\x7B\x01\x7A\x01\x7A\x01\x79\x01" 498 + "\x79\x01\x78\x01\x78\x01\x77\x01" 499 + "\x77\x01\x76\x01\x76\x01\x75\x01" 500 + "\x75\x01\x74\x01\x74\x01\x73\x01" 501 + "\x73\x01\x72\x01\x72\x01\x71\x01" 502 + "\x71\x01\x70\x01\x70\x01\x68\x01" 503 + "\x68\x01\x67\x01\x67\x01\x66\x01" 504 + "\x66\x01\x65\x01\x65\x01\x57\x01" 505 + "\x57\x01\x56\x01\x56\x01\x55\x01" 506 + "\x55\x01\x54\x01\x54\x01\x53\x01" 507 + "\x53\x01\x52\x01\x52\x01\x51\x01" 508 + "\x51\x01\x50\x01\x50\x01\x48\x01" 509 + "\x48\x01\x47\x01\x47\x01\x46\x01" 510 + "\x46\x01\x45\x01\x45\x01\x44\x01" 511 + "\x44\x01\x43\x01\x43\x01\x42\x01" 512 + "\x42\x01\x41\x01\x41\x01\x40\x01" 513 + "\x40\x01\x40\x01\x40\x01\x40\x01" 514 + "\x40\x01\x40\x01\x40\x01\x40\x01" 515 + "\x40\x01\x40\x01\x40\x01\x40\x01" 516 + "\x40\x01\x40\x01\x40\x01\x40\x01"); 517 + 518 + /* Write PDA according to certain rules. 519 + * 520 + * For every production data record, look for a previous setting in 521 + * the pda, and use that. 522 + * 523 + * For certain records, use defaults if they are not found in pda. 524 + */ 525 + int hermes_apply_pda_with_defaults(hermes_t *hw, 526 + const char *first_pdr, 527 + const __le16 *pda) 528 + { 529 + const struct pdr *pdr = (const struct pdr *) first_pdr; 530 + struct pdi *first_pdi = (struct pdi *) &pda[2]; 531 + struct pdi *pdi; 532 + struct pdi *default_pdi = NULL; 533 + struct pdi *outdoor_pdi; 534 + void *end = (void *)first_pdr + MAX_PDA_SIZE; 535 + int record_id; 536 + 537 + while (((void *)pdr < end) && 538 + (pdr_id(pdr) != PDI_END)) { 539 + /* 540 + * For spectrum_cs firmwares, 541 + * PDR area is currently not terminated by PDI_END. 542 + * It's followed by CRC records, which have the type 543 + * field where PDR has length. The type can be 0 or 1. 544 + */ 545 + if (pdr_len(pdr) < 2) 546 + break; 547 + record_id = pdr_id(pdr); 548 + 549 + pdi = hermes_find_pdi(first_pdi, record_id); 550 + if (pdi) 551 + printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n", 552 + record_id, pdi); 553 + 554 + switch (record_id) { 555 + case 0x110: /* Modem REFDAC values */ 556 + case 0x120: /* Modem VGDAC values */ 557 + outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1); 558 + default_pdi = NULL; 559 + if (outdoor_pdi) { 560 + pdi = outdoor_pdi; 561 + printk(KERN_DEBUG PFX 562 + "Using outdoor record 0x%04x at %p\n", 563 + record_id + 1, pdi); 564 + } 565 + break; 566 + case 0x5: /* HWIF Compatiblity */ 567 + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); 568 + break; 569 + case 0x108: /* PPPPSign */ 570 + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); 571 + break; 572 + case 0x109: /* PPPPProf */ 573 + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); 574 + break; 575 + case 0x150: /* Antenna diversity */ 576 + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); 577 + break; 578 + case 0x160: /* Modem VCO band Set-up */ 579 + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); 580 + break; 581 + case 0x161: /* Modem Rx Gain Table Values */ 582 + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); 583 + break; 584 + default: 585 + default_pdi = NULL; 586 + break; 587 + } 588 + if (!pdi && default_pdi) { 589 + /* Use default */ 590 + pdi = default_pdi; 591 + printk(KERN_DEBUG PFX 592 + "Using default record 0x%04x at %p\n", 593 + record_id, pdi); 594 + } 595 + 596 + if (pdi) { 597 + /* Lengths of the data in PDI and PDR must match */ 598 + if (pdi_len(pdi) == pdr_len(pdr)) { 599 + /* do the actual plugging */ 600 + hermes_aux_setaddr(hw, pdr_addr(pdr)); 601 + hermes_write_bytes(hw, HERMES_AUXDATA, 602 + pdi->data, pdi_len(pdi)); 603 + } 604 + } 605 + 606 + pdr++; 607 + } 608 + return 0; 609 + } 610 + EXPORT_SYMBOL(hermes_apply_pda_with_defaults);
+5
drivers/net/wireless/hermes_dld.h
··· 27 27 28 28 #include "hermes.h" 29 29 30 + int hermesi_program_init(hermes_t *hw, u32 offset); 31 + int hermesi_program_end(hermes_t *hw); 30 32 int hermes_program(hermes_t *hw, const char *first_block, const char *end); 31 33 32 34 int hermes_read_pda(hermes_t *hw, ··· 39 37 int hermes_apply_pda(hermes_t *hw, 40 38 const char *first_pdr, 41 39 const __le16 *pda); 40 + int hermes_apply_pda_with_defaults(hermes_t *hw, 41 + const char *first_pdr, 42 + const __le16 *pda); 42 43 43 44 size_t hermes_blocks_length(const char *first_block); 44 45