Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Hermes download helper.
0003  *
0004  * This helper:
0005  *  - is capable of writing to the volatile area of the hermes device
0006  *  - is currently not capable of writing to non-volatile areas
0007  *  - provide helpers to identify and update plugin data
0008  *  - is not capable of interpreting a fw image directly. That is up to
0009  *    the main card driver.
0010  *  - deals with Hermes I devices. It can probably be modified to deal
0011  *    with Hermes II devices
0012  *
0013  * Copyright (C) 2007, David Kilroy
0014  *
0015  * Plug data code slightly modified from spectrum_cs driver
0016  *    Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
0017  * Portions based on information in wl_lkm_718 Agere driver
0018  *    COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
0019  *
0020  * The contents of this file are subject to the Mozilla Public License
0021  * Version 1.1 (the "License"); you may not use this file except in
0022  * compliance with the License. You may obtain a copy of the License
0023  * at http://www.mozilla.org/MPL/
0024  *
0025  * Software distributed under the License is distributed on an "AS IS"
0026  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
0027  * the License for the specific language governing rights and
0028  * limitations under the License.
0029  *
0030  * Alternatively, the contents of this file may be used under the
0031  * terms of the GNU General Public License version 2 (the "GPL"), in
0032  * which case the provisions of the GPL are applicable instead of the
0033  * above.  If you wish to allow the use of your version of this file
0034  * only under the terms of the GPL and not to allow others to use your
0035  * version of this file under the MPL, indicate your decision by
0036  * deleting the provisions above and replace them with the notice and
0037  * other provisions required by the GPL.  If you do not delete the
0038  * provisions above, a recipient may use your version of this file
0039  * under either the MPL or the GPL.
0040  */
0041 
0042 #include <linux/module.h>
0043 #include <linux/delay.h>
0044 #include "hermes.h"
0045 #include "hermes_dld.h"
0046 
0047 #define PFX "hermes_dld: "
0048 
0049 /* End markers used in dblocks */
0050 #define PDI_END     0x00000000  /* End of PDA */
0051 #define BLOCK_END   0xFFFFFFFF  /* Last image block */
0052 #define TEXT_END    0x1A        /* End of text header */
0053 
0054 /*
0055  * The following structures have little-endian fields denoted by
0056  * the leading underscore.  Don't access them directly - use inline
0057  * functions defined below.
0058  */
0059 
0060 /*
0061  * The binary image to be downloaded consists of series of data blocks.
0062  * Each block has the following structure.
0063  */
0064 struct dblock {
0065     __le32 addr;        /* adapter address where to write the block */
0066     __le16 len;     /* length of the data only, in bytes */
0067     char data[];        /* data to be written */
0068 } __packed;
0069 
0070 /*
0071  * Plug Data References are located in the image after the last data
0072  * block.  They refer to areas in the adapter memory where the plug data
0073  * items with matching ID should be written.
0074  */
0075 struct pdr {
0076     __le32 id;      /* record ID */
0077     __le32 addr;        /* adapter address where to write the data */
0078     __le32 len;     /* expected length of the data, in bytes */
0079     char next[];        /* next PDR starts here */
0080 } __packed;
0081 
0082 /*
0083  * Plug Data Items are located in the EEPROM read from the adapter by
0084  * primary firmware.  They refer to the device-specific data that should
0085  * be plugged into the secondary firmware.
0086  */
0087 struct pdi {
0088     __le16 len;     /* length of ID and data, in words */
0089     __le16 id;      /* record ID */
0090     char data[];        /* plug data */
0091 } __packed;
0092 
0093 /*** FW data block access functions ***/
0094 
0095 static inline u32
0096 dblock_addr(const struct dblock *blk)
0097 {
0098     return le32_to_cpu(blk->addr);
0099 }
0100 
0101 static inline u32
0102 dblock_len(const struct dblock *blk)
0103 {
0104     return le16_to_cpu(blk->len);
0105 }
0106 
0107 /*** PDR Access functions ***/
0108 
0109 static inline u32
0110 pdr_id(const struct pdr *pdr)
0111 {
0112     return le32_to_cpu(pdr->id);
0113 }
0114 
0115 static inline u32
0116 pdr_addr(const struct pdr *pdr)
0117 {
0118     return le32_to_cpu(pdr->addr);
0119 }
0120 
0121 static inline u32
0122 pdr_len(const struct pdr *pdr)
0123 {
0124     return le32_to_cpu(pdr->len);
0125 }
0126 
0127 /*** PDI Access functions ***/
0128 
0129 static inline u32
0130 pdi_id(const struct pdi *pdi)
0131 {
0132     return le16_to_cpu(pdi->id);
0133 }
0134 
0135 /* Return length of the data only, in bytes */
0136 static inline u32
0137 pdi_len(const struct pdi *pdi)
0138 {
0139     return 2 * (le16_to_cpu(pdi->len) - 1);
0140 }
0141 
0142 /*** Plug Data Functions ***/
0143 
0144 /*
0145  * Scan PDR for the record with the specified RECORD_ID.
0146  * If it's not found, return NULL.
0147  */
0148 static const struct pdr *
0149 hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
0150 {
0151     const struct pdr *pdr = first_pdr;
0152 
0153     end -= sizeof(struct pdr);
0154 
0155     while (((void *) pdr <= end) &&
0156            (pdr_id(pdr) != PDI_END)) {
0157         /*
0158          * PDR area is currently not terminated by PDI_END.
0159          * It's followed by CRC records, which have the type
0160          * field where PDR has length.  The type can be 0 or 1.
0161          */
0162         if (pdr_len(pdr) < 2)
0163             return NULL;
0164 
0165         /* If the record ID matches, we are done */
0166         if (pdr_id(pdr) == record_id)
0167             return pdr;
0168 
0169         pdr = (struct pdr *) pdr->next;
0170     }
0171     return NULL;
0172 }
0173 
0174 /* Scan production data items for a particular entry */
0175 static const struct pdi *
0176 hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
0177 {
0178     const struct pdi *pdi = first_pdi;
0179 
0180     end -= sizeof(struct pdi);
0181 
0182     while (((void *) pdi <= end) &&
0183            (pdi_id(pdi) != PDI_END)) {
0184 
0185         /* If the record ID matches, we are done */
0186         if (pdi_id(pdi) == record_id)
0187             return pdi;
0188 
0189         pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
0190     }
0191     return NULL;
0192 }
0193 
0194 /* Process one Plug Data Item - find corresponding PDR and plug it */
0195 static int
0196 hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr,
0197         const struct pdi *pdi, const void *pdr_end)
0198 {
0199     const struct pdr *pdr;
0200 
0201     /* Find the PDR corresponding to this PDI */
0202     pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end);
0203 
0204     /* No match is found, safe to ignore */
0205     if (!pdr)
0206         return 0;
0207 
0208     /* Lengths of the data in PDI and PDR must match */
0209     if (pdi_len(pdi) != pdr_len(pdr))
0210         return -EINVAL;
0211 
0212     /* do the actual plugging */
0213     hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
0214 
0215     return 0;
0216 }
0217 
0218 /* Parse PDA and write the records into the adapter
0219  *
0220  * Attempt to write every records that is in the specified pda
0221  * which also has a valid production data record for the firmware.
0222  */
0223 int hermes_apply_pda(struct hermes *hw,
0224              const char *first_pdr,
0225              const void *pdr_end,
0226              const __le16 *pda,
0227              const void *pda_end)
0228 {
0229     int ret;
0230     const struct pdi *pdi;
0231     const struct pdr *pdr;
0232 
0233     pdr = (const struct pdr *) first_pdr;
0234     pda_end -= sizeof(struct pdi);
0235 
0236     /* Go through every PDI and plug them into the adapter */
0237     pdi = (const struct pdi *) (pda + 2);
0238     while (((void *) pdi <= pda_end) &&
0239            (pdi_id(pdi) != PDI_END)) {
0240         ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
0241         if (ret)
0242             return ret;
0243 
0244         /* Increment to the next PDI */
0245         pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
0246     }
0247     return 0;
0248 }
0249 
0250 /* Identify the total number of bytes in all blocks
0251  * including the header data.
0252  */
0253 size_t
0254 hermes_blocks_length(const char *first_block, const void *end)
0255 {
0256     const struct dblock *blk = (const struct dblock *) first_block;
0257     int total_len = 0;
0258     int len;
0259 
0260     end -= sizeof(*blk);
0261 
0262     /* Skip all blocks to locate Plug Data References
0263      * (Spectrum CS) */
0264     while (((void *) blk <= end) &&
0265            (dblock_addr(blk) != BLOCK_END)) {
0266         len = dblock_len(blk);
0267         total_len += sizeof(*blk) + len;
0268         blk = (struct dblock *) &blk->data[len];
0269     }
0270 
0271     return total_len;
0272 }
0273 
0274 /*** Hermes programming ***/
0275 
0276 /* Program the data blocks */
0277 int hermes_program(struct hermes *hw, const char *first_block, const void *end)
0278 {
0279     const struct dblock *blk;
0280     u32 blkaddr;
0281     u32 blklen;
0282     int err = 0;
0283 
0284     blk = (const struct dblock *) first_block;
0285 
0286     if ((void *) blk > (end - sizeof(*blk)))
0287         return -EIO;
0288 
0289     blkaddr = dblock_addr(blk);
0290     blklen = dblock_len(blk);
0291 
0292     while ((blkaddr != BLOCK_END) &&
0293            (((void *) blk + blklen) <= end)) {
0294         pr_debug(PFX "Programming block of length %d "
0295              "to address 0x%08x\n", blklen, blkaddr);
0296 
0297         err = hw->ops->program(hw, blk->data, blkaddr, blklen);
0298         if (err)
0299             break;
0300 
0301         blk = (const struct dblock *) &blk->data[blklen];
0302 
0303         if ((void *) blk > (end - sizeof(*blk)))
0304             return -EIO;
0305 
0306         blkaddr = dblock_addr(blk);
0307         blklen = dblock_len(blk);
0308     }
0309     return err;
0310 }
0311 
0312 /*** Default plugging data for Hermes I ***/
0313 /* Values from wl_lkm_718/hcf/dhf.c */
0314 
0315 #define DEFINE_DEFAULT_PDR(pid, length, data)               \
0316 static const struct {                           \
0317     __le16 len;                         \
0318     __le16 id;                          \
0319     u8 val[length];                         \
0320 } __packed default_pdr_data_##pid = {           \
0321     cpu_to_le16((sizeof(default_pdr_data_##pid)/            \
0322                 sizeof(__le16)) - 1),           \
0323     cpu_to_le16(pid),                       \
0324     data                                \
0325 }
0326 
0327 #define DEFAULT_PDR(pid) default_pdr_data_##pid
0328 
0329 /*  HWIF Compatibility */
0330 DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
0331 
0332 /* PPPPSign */
0333 DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
0334 
0335 /* PPPPProf */
0336 DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
0337 
0338 /* Antenna diversity */
0339 DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
0340 
0341 /* Modem VCO band Set-up */
0342 DEFINE_DEFAULT_PDR(0x0160, 28,
0343            "\x00\x00\x00\x00\x00\x00\x00\x00"
0344            "\x00\x00\x00\x00\x00\x00\x00\x00"
0345            "\x00\x00\x00\x00\x00\x00\x00\x00"
0346            "\x00\x00\x00\x00");
0347 
0348 /* Modem Rx Gain Table Values */
0349 DEFINE_DEFAULT_PDR(0x0161, 256,
0350            "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
0351            "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
0352            "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
0353            "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
0354            "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
0355            "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
0356            "\x3B\x01\x3A\01\x3A\x01\x39\x01"
0357            "\x39\x01\x38\01\x38\x01\x37\x01"
0358            "\x37\x01\x36\01\x36\x01\x35\x01"
0359            "\x35\x01\x34\01\x34\x01\x33\x01"
0360            "\x33\x01\x32\x01\x32\x01\x31\x01"
0361            "\x31\x01\x30\x01\x30\x01\x7B\x01"
0362            "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
0363            "\x79\x01\x78\x01\x78\x01\x77\x01"
0364            "\x77\x01\x76\x01\x76\x01\x75\x01"
0365            "\x75\x01\x74\x01\x74\x01\x73\x01"
0366            "\x73\x01\x72\x01\x72\x01\x71\x01"
0367            "\x71\x01\x70\x01\x70\x01\x68\x01"
0368            "\x68\x01\x67\x01\x67\x01\x66\x01"
0369            "\x66\x01\x65\x01\x65\x01\x57\x01"
0370            "\x57\x01\x56\x01\x56\x01\x55\x01"
0371            "\x55\x01\x54\x01\x54\x01\x53\x01"
0372            "\x53\x01\x52\x01\x52\x01\x51\x01"
0373            "\x51\x01\x50\x01\x50\x01\x48\x01"
0374            "\x48\x01\x47\x01\x47\x01\x46\x01"
0375            "\x46\x01\x45\x01\x45\x01\x44\x01"
0376            "\x44\x01\x43\x01\x43\x01\x42\x01"
0377            "\x42\x01\x41\x01\x41\x01\x40\x01"
0378            "\x40\x01\x40\x01\x40\x01\x40\x01"
0379            "\x40\x01\x40\x01\x40\x01\x40\x01"
0380            "\x40\x01\x40\x01\x40\x01\x40\x01"
0381            "\x40\x01\x40\x01\x40\x01\x40\x01");
0382 
0383 /* Write PDA according to certain rules.
0384  *
0385  * For every production data record, look for a previous setting in
0386  * the pda, and use that.
0387  *
0388  * For certain records, use defaults if they are not found in pda.
0389  */
0390 int hermes_apply_pda_with_defaults(struct hermes *hw,
0391                    const char *first_pdr,
0392                    const void *pdr_end,
0393                    const __le16 *pda,
0394                    const void *pda_end)
0395 {
0396     const struct pdr *pdr = (const struct pdr *) first_pdr;
0397     const struct pdi *first_pdi = (const struct pdi *) &pda[2];
0398     const struct pdi *pdi;
0399     const struct pdi *default_pdi = NULL;
0400     const struct pdi *outdoor_pdi;
0401     int record_id;
0402 
0403     pdr_end -= sizeof(struct pdr);
0404 
0405     while (((void *) pdr <= pdr_end) &&
0406            (pdr_id(pdr) != PDI_END)) {
0407         /*
0408          * For spectrum_cs firmwares,
0409          * PDR area is currently not terminated by PDI_END.
0410          * It's followed by CRC records, which have the type
0411          * field where PDR has length.  The type can be 0 or 1.
0412          */
0413         if (pdr_len(pdr) < 2)
0414             break;
0415         record_id = pdr_id(pdr);
0416 
0417         pdi = hermes_find_pdi(first_pdi, record_id, pda_end);
0418         if (pdi)
0419             pr_debug(PFX "Found record 0x%04x at %p\n",
0420                  record_id, pdi);
0421 
0422         switch (record_id) {
0423         case 0x110: /* Modem REFDAC values */
0424         case 0x120: /* Modem VGDAC values */
0425             outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1,
0426                               pda_end);
0427             default_pdi = NULL;
0428             if (outdoor_pdi) {
0429                 pdi = outdoor_pdi;
0430                 pr_debug(PFX
0431                      "Using outdoor record 0x%04x at %p\n",
0432                      record_id + 1, pdi);
0433             }
0434             break;
0435         case 0x5: /*  HWIF Compatibility */
0436             default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
0437             break;
0438         case 0x108: /* PPPPSign */
0439             default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
0440             break;
0441         case 0x109: /* PPPPProf */
0442             default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
0443             break;
0444         case 0x150: /* Antenna diversity */
0445             default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
0446             break;
0447         case 0x160: /* Modem VCO band Set-up */
0448             default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
0449             break;
0450         case 0x161: /* Modem Rx Gain Table Values */
0451             default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
0452             break;
0453         default:
0454             default_pdi = NULL;
0455             break;
0456         }
0457         if (!pdi && default_pdi) {
0458             /* Use default */
0459             pdi = default_pdi;
0460             pr_debug(PFX "Using default record 0x%04x at %p\n",
0461                  record_id, pdi);
0462         }
0463 
0464         if (pdi) {
0465             /* Lengths of the data in PDI and PDR must match */
0466             if ((pdi_len(pdi) == pdr_len(pdr)) &&
0467                 ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
0468                 /* do the actual plugging */
0469                 hw->ops->program(hw, pdi->data, pdr_addr(pdr),
0470                          pdi_len(pdi));
0471             }
0472         }
0473 
0474         pdr++;
0475     }
0476     return 0;
0477 }