hermes_dld.c (13078B)
1/* 2 * Hermes download helper. 3 * 4 * This helper: 5 * - is capable of writing to the volatile area of the hermes device 6 * - is currently not capable of writing to non-volatile areas 7 * - provide helpers to identify and update plugin data 8 * - is not capable of interpreting a fw image directly. That is up to 9 * the main card driver. 10 * - deals with Hermes I devices. It can probably be modified to deal 11 * with Hermes II devices 12 * 13 * Copyright (C) 2007, David Kilroy 14 * 15 * Plug data code slightly modified from spectrum_cs driver 16 * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org> 17 * Portions based on information in wl_lkm_718 Agere driver 18 * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved 19 * 20 * The contents of this file are subject to the Mozilla Public License 21 * Version 1.1 (the "License"); you may not use this file except in 22 * compliance with the License. You may obtain a copy of the License 23 * at http://www.mozilla.org/MPL/ 24 * 25 * Software distributed under the License is distributed on an "AS IS" 26 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 27 * the License for the specific language governing rights and 28 * limitations under the License. 29 * 30 * Alternatively, the contents of this file may be used under the 31 * terms of the GNU General Public License version 2 (the "GPL"), in 32 * which case the provisions of the GPL are applicable instead of the 33 * above. If you wish to allow the use of your version of this file 34 * only under the terms of the GPL and not to allow others to use your 35 * version of this file under the MPL, indicate your decision by 36 * deleting the provisions above and replace them with the notice and 37 * other provisions required by the GPL. If you do not delete the 38 * provisions above, a recipient may use your version of this file 39 * under either the MPL or the GPL. 40 */ 41 42#include <linux/module.h> 43#include <linux/delay.h> 44#include "hermes.h" 45#include "hermes_dld.h" 46 47#define PFX "hermes_dld: " 48 49/* End markers used in dblocks */ 50#define PDI_END 0x00000000 /* End of PDA */ 51#define BLOCK_END 0xFFFFFFFF /* Last image block */ 52#define TEXT_END 0x1A /* End of text header */ 53 54/* 55 * The following structures have little-endian fields denoted by 56 * the leading underscore. Don't access them directly - use inline 57 * functions defined below. 58 */ 59 60/* 61 * The binary image to be downloaded consists of series of data blocks. 62 * Each block has the following structure. 63 */ 64struct dblock { 65 __le32 addr; /* adapter address where to write the block */ 66 __le16 len; /* length of the data only, in bytes */ 67 char data[]; /* data to be written */ 68} __packed; 69 70/* 71 * Plug Data References are located in the image after the last data 72 * block. They refer to areas in the adapter memory where the plug data 73 * items with matching ID should be written. 74 */ 75struct pdr { 76 __le32 id; /* record ID */ 77 __le32 addr; /* adapter address where to write the data */ 78 __le32 len; /* expected length of the data, in bytes */ 79 char next[]; /* next PDR starts here */ 80} __packed; 81 82/* 83 * Plug Data Items are located in the EEPROM read from the adapter by 84 * primary firmware. They refer to the device-specific data that should 85 * be plugged into the secondary firmware. 86 */ 87struct pdi { 88 __le16 len; /* length of ID and data, in words */ 89 __le16 id; /* record ID */ 90 char data[]; /* plug data */ 91} __packed; 92 93/*** FW data block access functions ***/ 94 95static inline u32 96dblock_addr(const struct dblock *blk) 97{ 98 return le32_to_cpu(blk->addr); 99} 100 101static inline u32 102dblock_len(const struct dblock *blk) 103{ 104 return le16_to_cpu(blk->len); 105} 106 107/*** PDR Access functions ***/ 108 109static inline u32 110pdr_id(const struct pdr *pdr) 111{ 112 return le32_to_cpu(pdr->id); 113} 114 115static inline u32 116pdr_addr(const struct pdr *pdr) 117{ 118 return le32_to_cpu(pdr->addr); 119} 120 121static inline u32 122pdr_len(const struct pdr *pdr) 123{ 124 return le32_to_cpu(pdr->len); 125} 126 127/*** PDI Access functions ***/ 128 129static inline u32 130pdi_id(const struct pdi *pdi) 131{ 132 return le16_to_cpu(pdi->id); 133} 134 135/* Return length of the data only, in bytes */ 136static inline u32 137pdi_len(const struct pdi *pdi) 138{ 139 return 2 * (le16_to_cpu(pdi->len) - 1); 140} 141 142/*** Plug Data Functions ***/ 143 144/* 145 * Scan PDR for the record with the specified RECORD_ID. 146 * If it's not found, return NULL. 147 */ 148static const struct pdr * 149hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end) 150{ 151 const struct pdr *pdr = first_pdr; 152 153 end -= sizeof(struct pdr); 154 155 while (((void *) pdr <= end) && 156 (pdr_id(pdr) != PDI_END)) { 157 /* 158 * PDR area is currently not terminated by PDI_END. 159 * It's followed by CRC records, which have the type 160 * field where PDR has length. The type can be 0 or 1. 161 */ 162 if (pdr_len(pdr) < 2) 163 return NULL; 164 165 /* If the record ID matches, we are done */ 166 if (pdr_id(pdr) == record_id) 167 return pdr; 168 169 pdr = (struct pdr *) pdr->next; 170 } 171 return NULL; 172} 173 174/* Scan production data items for a particular entry */ 175static const struct pdi * 176hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end) 177{ 178 const struct pdi *pdi = first_pdi; 179 180 end -= sizeof(struct pdi); 181 182 while (((void *) pdi <= end) && 183 (pdi_id(pdi) != PDI_END)) { 184 185 /* If the record ID matches, we are done */ 186 if (pdi_id(pdi) == record_id) 187 return pdi; 188 189 pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; 190 } 191 return NULL; 192} 193 194/* Process one Plug Data Item - find corresponding PDR and plug it */ 195static int 196hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr, 197 const struct pdi *pdi, const void *pdr_end) 198{ 199 const struct pdr *pdr; 200 201 /* Find the PDR corresponding to this PDI */ 202 pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end); 203 204 /* No match is found, safe to ignore */ 205 if (!pdr) 206 return 0; 207 208 /* Lengths of the data in PDI and PDR must match */ 209 if (pdi_len(pdi) != pdr_len(pdr)) 210 return -EINVAL; 211 212 /* do the actual plugging */ 213 hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); 214 215 return 0; 216} 217 218/* Parse PDA and write the records into the adapter 219 * 220 * Attempt to write every records that is in the specified pda 221 * which also has a valid production data record for the firmware. 222 */ 223int hermes_apply_pda(struct hermes *hw, 224 const char *first_pdr, 225 const void *pdr_end, 226 const __le16 *pda, 227 const void *pda_end) 228{ 229 int ret; 230 const struct pdi *pdi; 231 const struct pdr *pdr; 232 233 pdr = (const struct pdr *) first_pdr; 234 pda_end -= sizeof(struct pdi); 235 236 /* Go through every PDI and plug them into the adapter */ 237 pdi = (const struct pdi *) (pda + 2); 238 while (((void *) pdi <= pda_end) && 239 (pdi_id(pdi) != PDI_END)) { 240 ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end); 241 if (ret) 242 return ret; 243 244 /* Increment to the next PDI */ 245 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)]; 246 } 247 return 0; 248} 249 250/* Identify the total number of bytes in all blocks 251 * including the header data. 252 */ 253size_t 254hermes_blocks_length(const char *first_block, const void *end) 255{ 256 const struct dblock *blk = (const struct dblock *) first_block; 257 int total_len = 0; 258 int len; 259 260 end -= sizeof(*blk); 261 262 /* Skip all blocks to locate Plug Data References 263 * (Spectrum CS) */ 264 while (((void *) blk <= end) && 265 (dblock_addr(blk) != BLOCK_END)) { 266 len = dblock_len(blk); 267 total_len += sizeof(*blk) + len; 268 blk = (struct dblock *) &blk->data[len]; 269 } 270 271 return total_len; 272} 273 274/*** Hermes programming ***/ 275 276/* Program the data blocks */ 277int hermes_program(struct hermes *hw, const char *first_block, const void *end) 278{ 279 const struct dblock *blk; 280 u32 blkaddr; 281 u32 blklen; 282 int err = 0; 283 284 blk = (const struct dblock *) first_block; 285 286 if ((void *) blk > (end - sizeof(*blk))) 287 return -EIO; 288 289 blkaddr = dblock_addr(blk); 290 blklen = dblock_len(blk); 291 292 while ((blkaddr != BLOCK_END) && 293 (((void *) blk + blklen) <= end)) { 294 pr_debug(PFX "Programming block of length %d " 295 "to address 0x%08x\n", blklen, blkaddr); 296 297 err = hw->ops->program(hw, blk->data, blkaddr, blklen); 298 if (err) 299 break; 300 301 blk = (const struct dblock *) &blk->data[blklen]; 302 303 if ((void *) blk > (end - sizeof(*blk))) 304 return -EIO; 305 306 blkaddr = dblock_addr(blk); 307 blklen = dblock_len(blk); 308 } 309 return err; 310} 311 312/*** Default plugging data for Hermes I ***/ 313/* Values from wl_lkm_718/hcf/dhf.c */ 314 315#define DEFINE_DEFAULT_PDR(pid, length, data) \ 316static const struct { \ 317 __le16 len; \ 318 __le16 id; \ 319 u8 val[length]; \ 320} __packed default_pdr_data_##pid = { \ 321 cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ 322 sizeof(__le16)) - 1), \ 323 cpu_to_le16(pid), \ 324 data \ 325} 326 327#define DEFAULT_PDR(pid) default_pdr_data_##pid 328 329/* HWIF Compatibility */ 330DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); 331 332/* PPPPSign */ 333DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); 334 335/* PPPPProf */ 336DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); 337 338/* Antenna diversity */ 339DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); 340 341/* Modem VCO band Set-up */ 342DEFINE_DEFAULT_PDR(0x0160, 28, 343 "\x00\x00\x00\x00\x00\x00\x00\x00" 344 "\x00\x00\x00\x00\x00\x00\x00\x00" 345 "\x00\x00\x00\x00\x00\x00\x00\x00" 346 "\x00\x00\x00\x00"); 347 348/* Modem Rx Gain Table Values */ 349DEFINE_DEFAULT_PDR(0x0161, 256, 350 "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 351 "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 352 "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 353 "\x3F\x01\x3F\01\x3F\x01\x3F\x01" 354 "\x3F\x01\x3E\01\x3E\x01\x3D\x01" 355 "\x3D\x01\x3C\01\x3C\x01\x3B\x01" 356 "\x3B\x01\x3A\01\x3A\x01\x39\x01" 357 "\x39\x01\x38\01\x38\x01\x37\x01" 358 "\x37\x01\x36\01\x36\x01\x35\x01" 359 "\x35\x01\x34\01\x34\x01\x33\x01" 360 "\x33\x01\x32\x01\x32\x01\x31\x01" 361 "\x31\x01\x30\x01\x30\x01\x7B\x01" 362 "\x7B\x01\x7A\x01\x7A\x01\x79\x01" 363 "\x79\x01\x78\x01\x78\x01\x77\x01" 364 "\x77\x01\x76\x01\x76\x01\x75\x01" 365 "\x75\x01\x74\x01\x74\x01\x73\x01" 366 "\x73\x01\x72\x01\x72\x01\x71\x01" 367 "\x71\x01\x70\x01\x70\x01\x68\x01" 368 "\x68\x01\x67\x01\x67\x01\x66\x01" 369 "\x66\x01\x65\x01\x65\x01\x57\x01" 370 "\x57\x01\x56\x01\x56\x01\x55\x01" 371 "\x55\x01\x54\x01\x54\x01\x53\x01" 372 "\x53\x01\x52\x01\x52\x01\x51\x01" 373 "\x51\x01\x50\x01\x50\x01\x48\x01" 374 "\x48\x01\x47\x01\x47\x01\x46\x01" 375 "\x46\x01\x45\x01\x45\x01\x44\x01" 376 "\x44\x01\x43\x01\x43\x01\x42\x01" 377 "\x42\x01\x41\x01\x41\x01\x40\x01" 378 "\x40\x01\x40\x01\x40\x01\x40\x01" 379 "\x40\x01\x40\x01\x40\x01\x40\x01" 380 "\x40\x01\x40\x01\x40\x01\x40\x01" 381 "\x40\x01\x40\x01\x40\x01\x40\x01"); 382 383/* Write PDA according to certain rules. 384 * 385 * For every production data record, look for a previous setting in 386 * the pda, and use that. 387 * 388 * For certain records, use defaults if they are not found in pda. 389 */ 390int hermes_apply_pda_with_defaults(struct hermes *hw, 391 const char *first_pdr, 392 const void *pdr_end, 393 const __le16 *pda, 394 const void *pda_end) 395{ 396 const struct pdr *pdr = (const struct pdr *) first_pdr; 397 const struct pdi *first_pdi = (const struct pdi *) &pda[2]; 398 const struct pdi *pdi; 399 const struct pdi *default_pdi = NULL; 400 const struct pdi *outdoor_pdi; 401 int record_id; 402 403 pdr_end -= sizeof(struct pdr); 404 405 while (((void *) pdr <= pdr_end) && 406 (pdr_id(pdr) != PDI_END)) { 407 /* 408 * For spectrum_cs firmwares, 409 * PDR area is currently not terminated by PDI_END. 410 * It's followed by CRC records, which have the type 411 * field where PDR has length. The type can be 0 or 1. 412 */ 413 if (pdr_len(pdr) < 2) 414 break; 415 record_id = pdr_id(pdr); 416 417 pdi = hermes_find_pdi(first_pdi, record_id, pda_end); 418 if (pdi) 419 pr_debug(PFX "Found record 0x%04x at %p\n", 420 record_id, pdi); 421 422 switch (record_id) { 423 case 0x110: /* Modem REFDAC values */ 424 case 0x120: /* Modem VGDAC values */ 425 outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1, 426 pda_end); 427 default_pdi = NULL; 428 if (outdoor_pdi) { 429 pdi = outdoor_pdi; 430 pr_debug(PFX 431 "Using outdoor record 0x%04x at %p\n", 432 record_id + 1, pdi); 433 } 434 break; 435 case 0x5: /* HWIF Compatibility */ 436 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); 437 break; 438 case 0x108: /* PPPPSign */ 439 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); 440 break; 441 case 0x109: /* PPPPProf */ 442 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); 443 break; 444 case 0x150: /* Antenna diversity */ 445 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); 446 break; 447 case 0x160: /* Modem VCO band Set-up */ 448 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); 449 break; 450 case 0x161: /* Modem Rx Gain Table Values */ 451 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); 452 break; 453 default: 454 default_pdi = NULL; 455 break; 456 } 457 if (!pdi && default_pdi) { 458 /* Use default */ 459 pdi = default_pdi; 460 pr_debug(PFX "Using default record 0x%04x at %p\n", 461 record_id, pdi); 462 } 463 464 if (pdi) { 465 /* Lengths of the data in PDI and PDR must match */ 466 if ((pdi_len(pdi) == pdr_len(pdr)) && 467 ((void *) pdi->data + pdi_len(pdi) < pda_end)) { 468 /* do the actual plugging */ 469 hw->ops->program(hw, pdi->data, pdr_addr(pdr), 470 pdi_len(pdi)); 471 } 472 } 473 474 pdr++; 475 } 476 return 0; 477}