From: David Kilroy Date: Thu, 21 Aug 2008 22:27:53 +0000 (+0100) Subject: orinoco: Extend hermes_dld routines for Agere firmware X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=8f5ae73c5366128d3800cf9765507422bcf1ef96;p=GitHub%2Fmt8127%2Fandroid_kernel_alcatel_ttab.git 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 Signed-off-by: John W. Linville --- diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c index 22ae79dae41e..d8c626e61a3a 100644 --- a/drivers/net/wireless/hermes_dld.c +++ b/drivers/net/wireless/hermes_dld.c @@ -70,6 +70,12 @@ MODULE_LICENSE("Dual MPL/GPL"); #define HERMES_AUX_PW1 0xDC23 #define HERMES_AUX_PW2 0xBA45 +/* HERMES_CMD_DOWNLD */ +#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) + /* End markers used in dblocks */ #define PDI_END 0x00000000 /* End of PDA */ #define BLOCK_END 0xFFFFFFFF /* Last image block */ @@ -247,6 +253,23 @@ hermes_find_pdr(struct pdr *first_pdr, u32 record_id) return NULL; } +/* Scan production data items for a particular entry */ +static struct pdi * +hermes_find_pdi(struct pdi *first_pdi, u32 record_id) +{ + struct pdi *pdi = first_pdi; + + while (pdi_id(pdi) != PDI_END) { + + /* If the record ID matches, we are done */ + if (pdi_id(pdi) == record_id) + return pdi; + + pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; + } + return NULL; +} + /* Process one Plug Data Item - find corresponding PDR and plug it */ static int hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) @@ -290,6 +313,15 @@ int hermes_read_pda(hermes_t *hw, ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); if (ret) return ret; + } else { + /* wl_lkm does not include PDA size in the PDA area. + * We will pad the information into pda, so other routines + * don't have to be modified */ + pda[0] = cpu_to_le16(pda_len - 2); + /* Includes CFG_PROD_DATA but not itself */ + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ + data_len = pda_len - 4; + data = pda + 2; } /* Open auxiliary port */ @@ -370,6 +402,94 @@ EXPORT_SYMBOL(hermes_blocks_length); /*** Hermes programming ***/ +/* About to start programming data (Hermes I) + * offset is the entry point + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +int hermesi_program_init(hermes_t *hw, u32 offset) +{ + int err; + + /* Disable interrupts?*/ + /*hw->inten = 0x0;*/ + /*hermes_write_regn(hw, INTEN, 0);*/ + /*hermes_set_irqmask(hw, 0);*/ + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Using doicmd_wait rather than docmd_wait */ + err = hermes_doicmd_wait(hw, + 0x0100 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hermes_doicmd_wait(hw, + 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hermes_aux_control(hw, 1); + printk(KERN_DEBUG PFX "AUX enable returned %d\n", err); + + if (err) + return err; + + printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset); + err = hermes_doicmd_wait(hw, + HERMES_PROGRAM_ENABLE_VOLATILE, + offset & 0xFFFFu, + offset >> 16, + 0, + NULL); + printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n", + err); + + return err; +} +EXPORT_SYMBOL(hermesi_program_init); + +/* Done programming data (Hermes I) + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +int hermesi_program_end(hermes_t *hw) +{ + struct hermes_response resp; + int rc = 0; + int err; + + rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); + + printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, " + "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", + rc, resp.resp0, resp.resp1, resp.resp2); + + if ((rc == 0) && + ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) + rc = -EIO; + + err = hermes_aux_control(hw, 0); + printk(KERN_DEBUG PFX "AUX disable returned %d\n", err); + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Reinitialise, ignoring return */ + (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + + return rc ? rc : err; +} +EXPORT_SYMBOL(hermesi_program_end); + /* Program the data blocks */ int hermes_program(hermes_t *hw, const char *first_block, const char *end) { @@ -443,3 +563,168 @@ static void __exit exit_hermes_dld(void) module_init(init_hermes_dld); module_exit(exit_hermes_dld); + +/*** Default plugging data for Hermes I ***/ +/* Values from wl_lkm_718/hcf/dhf.c */ + +#define DEFINE_DEFAULT_PDR(pid, length, data) \ +static const struct { \ + __le16 len; \ + __le16 id; \ + u8 val[length]; \ +} __attribute__ ((packed)) default_pdr_data_##pid = { \ + __constant_cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ + sizeof(__le16)) - 1), \ + __constant_cpu_to_le16(pid), \ + data \ +} + +#define DEFAULT_PDR(pid) default_pdr_data_##pid + +/* HWIF Compatiblity */ +DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); + +/* PPPPSign */ +DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); + +/* PPPPProf */ +DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); + +/* Antenna diversity */ +DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); + +/* Modem VCO band Set-up */ +DEFINE_DEFAULT_PDR(0x0160, 28, + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00"); + +/* Modem Rx Gain Table Values */ +DEFINE_DEFAULT_PDR(0x0161, 256, + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3E\01\x3E\x01\x3D\x01" + "\x3D\x01\x3C\01\x3C\x01\x3B\x01" + "\x3B\x01\x3A\01\x3A\x01\x39\x01" + "\x39\x01\x38\01\x38\x01\x37\x01" + "\x37\x01\x36\01\x36\x01\x35\x01" + "\x35\x01\x34\01\x34\x01\x33\x01" + "\x33\x01\x32\x01\x32\x01\x31\x01" + "\x31\x01\x30\x01\x30\x01\x7B\x01" + "\x7B\x01\x7A\x01\x7A\x01\x79\x01" + "\x79\x01\x78\x01\x78\x01\x77\x01" + "\x77\x01\x76\x01\x76\x01\x75\x01" + "\x75\x01\x74\x01\x74\x01\x73\x01" + "\x73\x01\x72\x01\x72\x01\x71\x01" + "\x71\x01\x70\x01\x70\x01\x68\x01" + "\x68\x01\x67\x01\x67\x01\x66\x01" + "\x66\x01\x65\x01\x65\x01\x57\x01" + "\x57\x01\x56\x01\x56\x01\x55\x01" + "\x55\x01\x54\x01\x54\x01\x53\x01" + "\x53\x01\x52\x01\x52\x01\x51\x01" + "\x51\x01\x50\x01\x50\x01\x48\x01" + "\x48\x01\x47\x01\x47\x01\x46\x01" + "\x46\x01\x45\x01\x45\x01\x44\x01" + "\x44\x01\x43\x01\x43\x01\x42\x01" + "\x42\x01\x41\x01\x41\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01"); + +/* Write PDA according to certain rules. + * + * For every production data record, look for a previous setting in + * the pda, and use that. + * + * For certain records, use defaults if they are not found in pda. + */ +int hermes_apply_pda_with_defaults(hermes_t *hw, + const char *first_pdr, + const __le16 *pda) +{ + const struct pdr *pdr = (const struct pdr *) first_pdr; + struct pdi *first_pdi = (struct pdi *) &pda[2]; + struct pdi *pdi; + struct pdi *default_pdi = NULL; + struct pdi *outdoor_pdi; + void *end = (void *)first_pdr + MAX_PDA_SIZE; + int record_id; + + while (((void *)pdr < end) && + (pdr_id(pdr) != PDI_END)) { + /* + * For spectrum_cs firmwares, + * PDR area is currently not terminated by PDI_END. + * It's followed by CRC records, which have the type + * field where PDR has length. The type can be 0 or 1. + */ + if (pdr_len(pdr) < 2) + break; + record_id = pdr_id(pdr); + + pdi = hermes_find_pdi(first_pdi, record_id); + if (pdi) + printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n", + record_id, pdi); + + switch (record_id) { + case 0x110: /* Modem REFDAC values */ + case 0x120: /* Modem VGDAC values */ + outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1); + default_pdi = NULL; + if (outdoor_pdi) { + pdi = outdoor_pdi; + printk(KERN_DEBUG PFX + "Using outdoor record 0x%04x at %p\n", + record_id + 1, pdi); + } + break; + case 0x5: /* HWIF Compatiblity */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); + break; + case 0x108: /* PPPPSign */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); + break; + case 0x109: /* PPPPProf */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); + break; + case 0x150: /* Antenna diversity */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); + break; + case 0x160: /* Modem VCO band Set-up */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); + break; + case 0x161: /* Modem Rx Gain Table Values */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); + break; + default: + default_pdi = NULL; + break; + } + if (!pdi && default_pdi) { + /* Use default */ + pdi = default_pdi; + printk(KERN_DEBUG PFX + "Using default record 0x%04x at %p\n", + record_id, pdi); + } + + if (pdi) { + /* Lengths of the data in PDI and PDR must match */ + if (pdi_len(pdi) == pdr_len(pdr)) { + /* do the actual plugging */ + hermes_aux_setaddr(hw, pdr_addr(pdr)); + hermes_write_bytes(hw, HERMES_AUXDATA, + pdi->data, pdi_len(pdi)); + } + } + + pdr++; + } + return 0; +} +EXPORT_SYMBOL(hermes_apply_pda_with_defaults); diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h index af75c030b11b..6fcb26277999 100644 --- a/drivers/net/wireless/hermes_dld.h +++ b/drivers/net/wireless/hermes_dld.h @@ -27,6 +27,8 @@ #include "hermes.h" +int hermesi_program_init(hermes_t *hw, u32 offset); +int hermesi_program_end(hermes_t *hw); int hermes_program(hermes_t *hw, const char *first_block, const char *end); int hermes_read_pda(hermes_t *hw, @@ -37,6 +39,9 @@ int hermes_read_pda(hermes_t *hw, int hermes_apply_pda(hermes_t *hw, const char *first_pdr, const __le16 *pda); +int hermes_apply_pda_with_defaults(hermes_t *hw, + const char *first_pdr, + const __le16 *pda); size_t hermes_blocks_length(const char *first_block);