12be45b66SKalle Valo /*
22be45b66SKalle Valo * Hermes download helper.
32be45b66SKalle Valo *
42be45b66SKalle Valo * This helper:
52be45b66SKalle Valo * - is capable of writing to the volatile area of the hermes device
62be45b66SKalle Valo * - is currently not capable of writing to non-volatile areas
72be45b66SKalle Valo * - provide helpers to identify and update plugin data
82be45b66SKalle Valo * - is not capable of interpreting a fw image directly. That is up to
92be45b66SKalle Valo * the main card driver.
102be45b66SKalle Valo * - deals with Hermes I devices. It can probably be modified to deal
112be45b66SKalle Valo * with Hermes II devices
122be45b66SKalle Valo *
132be45b66SKalle Valo * Copyright (C) 2007, David Kilroy
142be45b66SKalle Valo *
152be45b66SKalle Valo * Plug data code slightly modified from spectrum_cs driver
162be45b66SKalle Valo * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
172be45b66SKalle Valo * Portions based on information in wl_lkm_718 Agere driver
182be45b66SKalle Valo * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
192be45b66SKalle Valo *
202be45b66SKalle Valo * The contents of this file are subject to the Mozilla Public License
212be45b66SKalle Valo * Version 1.1 (the "License"); you may not use this file except in
222be45b66SKalle Valo * compliance with the License. You may obtain a copy of the License
232be45b66SKalle Valo * at http://www.mozilla.org/MPL/
242be45b66SKalle Valo *
252be45b66SKalle Valo * Software distributed under the License is distributed on an "AS IS"
262be45b66SKalle Valo * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
272be45b66SKalle Valo * the License for the specific language governing rights and
282be45b66SKalle Valo * limitations under the License.
292be45b66SKalle Valo *
302be45b66SKalle Valo * Alternatively, the contents of this file may be used under the
312be45b66SKalle Valo * terms of the GNU General Public License version 2 (the "GPL"), in
322be45b66SKalle Valo * which case the provisions of the GPL are applicable instead of the
332be45b66SKalle Valo * above. If you wish to allow the use of your version of this file
342be45b66SKalle Valo * only under the terms of the GPL and not to allow others to use your
352be45b66SKalle Valo * version of this file under the MPL, indicate your decision by
362be45b66SKalle Valo * deleting the provisions above and replace them with the notice and
372be45b66SKalle Valo * other provisions required by the GPL. If you do not delete the
382be45b66SKalle Valo * provisions above, a recipient may use your version of this file
392be45b66SKalle Valo * under either the MPL or the GPL.
402be45b66SKalle Valo */
412be45b66SKalle Valo
422be45b66SKalle Valo #include <linux/module.h>
432be45b66SKalle Valo #include <linux/delay.h>
442be45b66SKalle Valo #include "hermes.h"
452be45b66SKalle Valo #include "hermes_dld.h"
462be45b66SKalle Valo
472be45b66SKalle Valo #define PFX "hermes_dld: "
482be45b66SKalle Valo
492be45b66SKalle Valo /* End markers used in dblocks */
502be45b66SKalle Valo #define PDI_END 0x00000000 /* End of PDA */
512be45b66SKalle Valo #define BLOCK_END 0xFFFFFFFF /* Last image block */
522be45b66SKalle Valo #define TEXT_END 0x1A /* End of text header */
532be45b66SKalle Valo
542be45b66SKalle Valo /*
552be45b66SKalle Valo * The following structures have little-endian fields denoted by
562be45b66SKalle Valo * the leading underscore. Don't access them directly - use inline
572be45b66SKalle Valo * functions defined below.
582be45b66SKalle Valo */
592be45b66SKalle Valo
602be45b66SKalle Valo /*
612be45b66SKalle Valo * The binary image to be downloaded consists of series of data blocks.
622be45b66SKalle Valo * Each block has the following structure.
632be45b66SKalle Valo */
642be45b66SKalle Valo struct dblock {
652be45b66SKalle Valo __le32 addr; /* adapter address where to write the block */
662be45b66SKalle Valo __le16 len; /* length of the data only, in bytes */
67*645aa87fSGustavo A. R. Silva char data[]; /* data to be written */
682be45b66SKalle Valo } __packed;
692be45b66SKalle Valo
702be45b66SKalle Valo /*
712be45b66SKalle Valo * Plug Data References are located in the image after the last data
722be45b66SKalle Valo * block. They refer to areas in the adapter memory where the plug data
732be45b66SKalle Valo * items with matching ID should be written.
742be45b66SKalle Valo */
752be45b66SKalle Valo struct pdr {
762be45b66SKalle Valo __le32 id; /* record ID */
772be45b66SKalle Valo __le32 addr; /* adapter address where to write the data */
782be45b66SKalle Valo __le32 len; /* expected length of the data, in bytes */
79*645aa87fSGustavo A. R. Silva char next[]; /* next PDR starts here */
802be45b66SKalle Valo } __packed;
812be45b66SKalle Valo
822be45b66SKalle Valo /*
832be45b66SKalle Valo * Plug Data Items are located in the EEPROM read from the adapter by
842be45b66SKalle Valo * primary firmware. They refer to the device-specific data that should
852be45b66SKalle Valo * be plugged into the secondary firmware.
862be45b66SKalle Valo */
872be45b66SKalle Valo struct pdi {
882be45b66SKalle Valo __le16 len; /* length of ID and data, in words */
892be45b66SKalle Valo __le16 id; /* record ID */
90*645aa87fSGustavo A. R. Silva char data[]; /* plug data */
912be45b66SKalle Valo } __packed;
922be45b66SKalle Valo
932be45b66SKalle Valo /*** FW data block access functions ***/
942be45b66SKalle Valo
952be45b66SKalle Valo static inline u32
dblock_addr(const struct dblock * blk)962be45b66SKalle Valo dblock_addr(const struct dblock *blk)
972be45b66SKalle Valo {
982be45b66SKalle Valo return le32_to_cpu(blk->addr);
992be45b66SKalle Valo }
1002be45b66SKalle Valo
1012be45b66SKalle Valo static inline u32
dblock_len(const struct dblock * blk)1022be45b66SKalle Valo dblock_len(const struct dblock *blk)
1032be45b66SKalle Valo {
1042be45b66SKalle Valo return le16_to_cpu(blk->len);
1052be45b66SKalle Valo }
1062be45b66SKalle Valo
1072be45b66SKalle Valo /*** PDR Access functions ***/
1082be45b66SKalle Valo
1092be45b66SKalle Valo static inline u32
pdr_id(const struct pdr * pdr)1102be45b66SKalle Valo pdr_id(const struct pdr *pdr)
1112be45b66SKalle Valo {
1122be45b66SKalle Valo return le32_to_cpu(pdr->id);
1132be45b66SKalle Valo }
1142be45b66SKalle Valo
1152be45b66SKalle Valo static inline u32
pdr_addr(const struct pdr * pdr)1162be45b66SKalle Valo pdr_addr(const struct pdr *pdr)
1172be45b66SKalle Valo {
1182be45b66SKalle Valo return le32_to_cpu(pdr->addr);
1192be45b66SKalle Valo }
1202be45b66SKalle Valo
1212be45b66SKalle Valo static inline u32
pdr_len(const struct pdr * pdr)1222be45b66SKalle Valo pdr_len(const struct pdr *pdr)
1232be45b66SKalle Valo {
1242be45b66SKalle Valo return le32_to_cpu(pdr->len);
1252be45b66SKalle Valo }
1262be45b66SKalle Valo
1272be45b66SKalle Valo /*** PDI Access functions ***/
1282be45b66SKalle Valo
1292be45b66SKalle Valo static inline u32
pdi_id(const struct pdi * pdi)1302be45b66SKalle Valo pdi_id(const struct pdi *pdi)
1312be45b66SKalle Valo {
1322be45b66SKalle Valo return le16_to_cpu(pdi->id);
1332be45b66SKalle Valo }
1342be45b66SKalle Valo
1352be45b66SKalle Valo /* Return length of the data only, in bytes */
1362be45b66SKalle Valo static inline u32
pdi_len(const struct pdi * pdi)1372be45b66SKalle Valo pdi_len(const struct pdi *pdi)
1382be45b66SKalle Valo {
1392be45b66SKalle Valo return 2 * (le16_to_cpu(pdi->len) - 1);
1402be45b66SKalle Valo }
1412be45b66SKalle Valo
1422be45b66SKalle Valo /*** Plug Data Functions ***/
1432be45b66SKalle Valo
1442be45b66SKalle Valo /*
1452be45b66SKalle Valo * Scan PDR for the record with the specified RECORD_ID.
1462be45b66SKalle Valo * If it's not found, return NULL.
1472be45b66SKalle Valo */
1482be45b66SKalle Valo static const struct pdr *
hermes_find_pdr(const struct pdr * first_pdr,u32 record_id,const void * end)1492be45b66SKalle Valo hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
1502be45b66SKalle Valo {
1512be45b66SKalle Valo const struct pdr *pdr = first_pdr;
1522be45b66SKalle Valo
1532be45b66SKalle Valo end -= sizeof(struct pdr);
1542be45b66SKalle Valo
1552be45b66SKalle Valo while (((void *) pdr <= end) &&
1562be45b66SKalle Valo (pdr_id(pdr) != PDI_END)) {
1572be45b66SKalle Valo /*
1582be45b66SKalle Valo * PDR area is currently not terminated by PDI_END.
1592be45b66SKalle Valo * It's followed by CRC records, which have the type
1602be45b66SKalle Valo * field where PDR has length. The type can be 0 or 1.
1612be45b66SKalle Valo */
1622be45b66SKalle Valo if (pdr_len(pdr) < 2)
1632be45b66SKalle Valo return NULL;
1642be45b66SKalle Valo
1652be45b66SKalle Valo /* If the record ID matches, we are done */
1662be45b66SKalle Valo if (pdr_id(pdr) == record_id)
1672be45b66SKalle Valo return pdr;
1682be45b66SKalle Valo
1692be45b66SKalle Valo pdr = (struct pdr *) pdr->next;
1702be45b66SKalle Valo }
1712be45b66SKalle Valo return NULL;
1722be45b66SKalle Valo }
1732be45b66SKalle Valo
1742be45b66SKalle Valo /* Scan production data items for a particular entry */
1752be45b66SKalle Valo static const struct pdi *
hermes_find_pdi(const struct pdi * first_pdi,u32 record_id,const void * end)1762be45b66SKalle Valo hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
1772be45b66SKalle Valo {
1782be45b66SKalle Valo const struct pdi *pdi = first_pdi;
1792be45b66SKalle Valo
1802be45b66SKalle Valo end -= sizeof(struct pdi);
1812be45b66SKalle Valo
1822be45b66SKalle Valo while (((void *) pdi <= end) &&
1832be45b66SKalle Valo (pdi_id(pdi) != PDI_END)) {
1842be45b66SKalle Valo
1852be45b66SKalle Valo /* If the record ID matches, we are done */
1862be45b66SKalle Valo if (pdi_id(pdi) == record_id)
1872be45b66SKalle Valo return pdi;
1882be45b66SKalle Valo
1892be45b66SKalle Valo pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
1902be45b66SKalle Valo }
1912be45b66SKalle Valo return NULL;
1922be45b66SKalle Valo }
1932be45b66SKalle Valo
1942be45b66SKalle Valo /* Process one Plug Data Item - find corresponding PDR and plug it */
1952be45b66SKalle Valo static int
hermes_plug_pdi(struct hermes * hw,const struct pdr * first_pdr,const struct pdi * pdi,const void * pdr_end)1962be45b66SKalle Valo hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr,
1972be45b66SKalle Valo const struct pdi *pdi, const void *pdr_end)
1982be45b66SKalle Valo {
1992be45b66SKalle Valo const struct pdr *pdr;
2002be45b66SKalle Valo
2012be45b66SKalle Valo /* Find the PDR corresponding to this PDI */
2022be45b66SKalle Valo pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end);
2032be45b66SKalle Valo
2042be45b66SKalle Valo /* No match is found, safe to ignore */
2052be45b66SKalle Valo if (!pdr)
2062be45b66SKalle Valo return 0;
2072be45b66SKalle Valo
2082be45b66SKalle Valo /* Lengths of the data in PDI and PDR must match */
2092be45b66SKalle Valo if (pdi_len(pdi) != pdr_len(pdr))
2102be45b66SKalle Valo return -EINVAL;
2112be45b66SKalle Valo
2122be45b66SKalle Valo /* do the actual plugging */
2132be45b66SKalle Valo hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
2142be45b66SKalle Valo
2152be45b66SKalle Valo return 0;
2162be45b66SKalle Valo }
2172be45b66SKalle Valo
2182be45b66SKalle Valo /* Parse PDA and write the records into the adapter
2192be45b66SKalle Valo *
2202be45b66SKalle Valo * Attempt to write every records that is in the specified pda
2212be45b66SKalle Valo * which also has a valid production data record for the firmware.
2222be45b66SKalle Valo */
hermes_apply_pda(struct hermes * hw,const char * first_pdr,const void * pdr_end,const __le16 * pda,const void * pda_end)2232be45b66SKalle Valo int hermes_apply_pda(struct hermes *hw,
2242be45b66SKalle Valo const char *first_pdr,
2252be45b66SKalle Valo const void *pdr_end,
2262be45b66SKalle Valo const __le16 *pda,
2272be45b66SKalle Valo const void *pda_end)
2282be45b66SKalle Valo {
2292be45b66SKalle Valo int ret;
2302be45b66SKalle Valo const struct pdi *pdi;
2312be45b66SKalle Valo const struct pdr *pdr;
2322be45b66SKalle Valo
2332be45b66SKalle Valo pdr = (const struct pdr *) first_pdr;
2342be45b66SKalle Valo pda_end -= sizeof(struct pdi);
2352be45b66SKalle Valo
2362be45b66SKalle Valo /* Go through every PDI and plug them into the adapter */
2372be45b66SKalle Valo pdi = (const struct pdi *) (pda + 2);
2382be45b66SKalle Valo while (((void *) pdi <= pda_end) &&
2392be45b66SKalle Valo (pdi_id(pdi) != PDI_END)) {
2402be45b66SKalle Valo ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
2412be45b66SKalle Valo if (ret)
2422be45b66SKalle Valo return ret;
2432be45b66SKalle Valo
2442be45b66SKalle Valo /* Increment to the next PDI */
2452be45b66SKalle Valo pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
2462be45b66SKalle Valo }
2472be45b66SKalle Valo return 0;
2482be45b66SKalle Valo }
2492be45b66SKalle Valo
2502be45b66SKalle Valo /* Identify the total number of bytes in all blocks
2512be45b66SKalle Valo * including the header data.
2522be45b66SKalle Valo */
2532be45b66SKalle Valo size_t
hermes_blocks_length(const char * first_block,const void * end)2542be45b66SKalle Valo hermes_blocks_length(const char *first_block, const void *end)
2552be45b66SKalle Valo {
2562be45b66SKalle Valo const struct dblock *blk = (const struct dblock *) first_block;
2572be45b66SKalle Valo int total_len = 0;
2582be45b66SKalle Valo int len;
2592be45b66SKalle Valo
2602be45b66SKalle Valo end -= sizeof(*blk);
2612be45b66SKalle Valo
2622be45b66SKalle Valo /* Skip all blocks to locate Plug Data References
2632be45b66SKalle Valo * (Spectrum CS) */
2642be45b66SKalle Valo while (((void *) blk <= end) &&
2652be45b66SKalle Valo (dblock_addr(blk) != BLOCK_END)) {
2662be45b66SKalle Valo len = dblock_len(blk);
2672be45b66SKalle Valo total_len += sizeof(*blk) + len;
2682be45b66SKalle Valo blk = (struct dblock *) &blk->data[len];
2692be45b66SKalle Valo }
2702be45b66SKalle Valo
2712be45b66SKalle Valo return total_len;
2722be45b66SKalle Valo }
2732be45b66SKalle Valo
2742be45b66SKalle Valo /*** Hermes programming ***/
2752be45b66SKalle Valo
2762be45b66SKalle Valo /* Program the data blocks */
hermes_program(struct hermes * hw,const char * first_block,const void * end)2772be45b66SKalle Valo int hermes_program(struct hermes *hw, const char *first_block, const void *end)
2782be45b66SKalle Valo {
2792be45b66SKalle Valo const struct dblock *blk;
2802be45b66SKalle Valo u32 blkaddr;
2812be45b66SKalle Valo u32 blklen;
2822be45b66SKalle Valo int err = 0;
2832be45b66SKalle Valo
2842be45b66SKalle Valo blk = (const struct dblock *) first_block;
2852be45b66SKalle Valo
2862be45b66SKalle Valo if ((void *) blk > (end - sizeof(*blk)))
2872be45b66SKalle Valo return -EIO;
2882be45b66SKalle Valo
2892be45b66SKalle Valo blkaddr = dblock_addr(blk);
2902be45b66SKalle Valo blklen = dblock_len(blk);
2912be45b66SKalle Valo
2922be45b66SKalle Valo while ((blkaddr != BLOCK_END) &&
2932be45b66SKalle Valo (((void *) blk + blklen) <= end)) {
2942be45b66SKalle Valo pr_debug(PFX "Programming block of length %d "
2952be45b66SKalle Valo "to address 0x%08x\n", blklen, blkaddr);
2962be45b66SKalle Valo
2972be45b66SKalle Valo err = hw->ops->program(hw, blk->data, blkaddr, blklen);
2982be45b66SKalle Valo if (err)
2992be45b66SKalle Valo break;
3002be45b66SKalle Valo
3012be45b66SKalle Valo blk = (const struct dblock *) &blk->data[blklen];
3022be45b66SKalle Valo
3032be45b66SKalle Valo if ((void *) blk > (end - sizeof(*blk)))
3042be45b66SKalle Valo return -EIO;
3052be45b66SKalle Valo
3062be45b66SKalle Valo blkaddr = dblock_addr(blk);
3072be45b66SKalle Valo blklen = dblock_len(blk);
3082be45b66SKalle Valo }
3092be45b66SKalle Valo return err;
3102be45b66SKalle Valo }
3112be45b66SKalle Valo
3122be45b66SKalle Valo /*** Default plugging data for Hermes I ***/
3132be45b66SKalle Valo /* Values from wl_lkm_718/hcf/dhf.c */
3142be45b66SKalle Valo
3152be45b66SKalle Valo #define DEFINE_DEFAULT_PDR(pid, length, data) \
3162be45b66SKalle Valo static const struct { \
3172be45b66SKalle Valo __le16 len; \
3182be45b66SKalle Valo __le16 id; \
3192be45b66SKalle Valo u8 val[length]; \
3202be45b66SKalle Valo } __packed default_pdr_data_##pid = { \
3212be45b66SKalle Valo cpu_to_le16((sizeof(default_pdr_data_##pid)/ \
3222be45b66SKalle Valo sizeof(__le16)) - 1), \
3232be45b66SKalle Valo cpu_to_le16(pid), \
3242be45b66SKalle Valo data \
3252be45b66SKalle Valo }
3262be45b66SKalle Valo
3272be45b66SKalle Valo #define DEFAULT_PDR(pid) default_pdr_data_##pid
3282be45b66SKalle Valo
3292be45b66SKalle Valo /* HWIF Compatibility */
3302be45b66SKalle Valo DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
3312be45b66SKalle Valo
3322be45b66SKalle Valo /* PPPPSign */
3332be45b66SKalle Valo DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
3342be45b66SKalle Valo
3352be45b66SKalle Valo /* PPPPProf */
3362be45b66SKalle Valo DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
3372be45b66SKalle Valo
3382be45b66SKalle Valo /* Antenna diversity */
3392be45b66SKalle Valo DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
3402be45b66SKalle Valo
3412be45b66SKalle Valo /* Modem VCO band Set-up */
3422be45b66SKalle Valo DEFINE_DEFAULT_PDR(0x0160, 28,
3432be45b66SKalle Valo "\x00\x00\x00\x00\x00\x00\x00\x00"
3442be45b66SKalle Valo "\x00\x00\x00\x00\x00\x00\x00\x00"
3452be45b66SKalle Valo "\x00\x00\x00\x00\x00\x00\x00\x00"
3462be45b66SKalle Valo "\x00\x00\x00\x00");
3472be45b66SKalle Valo
3482be45b66SKalle Valo /* Modem Rx Gain Table Values */
3492be45b66SKalle Valo DEFINE_DEFAULT_PDR(0x0161, 256,
3502be45b66SKalle Valo "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
3512be45b66SKalle Valo "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
3522be45b66SKalle Valo "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
3532be45b66SKalle Valo "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
3542be45b66SKalle Valo "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
3552be45b66SKalle Valo "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
3562be45b66SKalle Valo "\x3B\x01\x3A\01\x3A\x01\x39\x01"
3572be45b66SKalle Valo "\x39\x01\x38\01\x38\x01\x37\x01"
3582be45b66SKalle Valo "\x37\x01\x36\01\x36\x01\x35\x01"
3592be45b66SKalle Valo "\x35\x01\x34\01\x34\x01\x33\x01"
3602be45b66SKalle Valo "\x33\x01\x32\x01\x32\x01\x31\x01"
3612be45b66SKalle Valo "\x31\x01\x30\x01\x30\x01\x7B\x01"
3622be45b66SKalle Valo "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
3632be45b66SKalle Valo "\x79\x01\x78\x01\x78\x01\x77\x01"
3642be45b66SKalle Valo "\x77\x01\x76\x01\x76\x01\x75\x01"
3652be45b66SKalle Valo "\x75\x01\x74\x01\x74\x01\x73\x01"
3662be45b66SKalle Valo "\x73\x01\x72\x01\x72\x01\x71\x01"
3672be45b66SKalle Valo "\x71\x01\x70\x01\x70\x01\x68\x01"
3682be45b66SKalle Valo "\x68\x01\x67\x01\x67\x01\x66\x01"
3692be45b66SKalle Valo "\x66\x01\x65\x01\x65\x01\x57\x01"
3702be45b66SKalle Valo "\x57\x01\x56\x01\x56\x01\x55\x01"
3712be45b66SKalle Valo "\x55\x01\x54\x01\x54\x01\x53\x01"
3722be45b66SKalle Valo "\x53\x01\x52\x01\x52\x01\x51\x01"
3732be45b66SKalle Valo "\x51\x01\x50\x01\x50\x01\x48\x01"
3742be45b66SKalle Valo "\x48\x01\x47\x01\x47\x01\x46\x01"
3752be45b66SKalle Valo "\x46\x01\x45\x01\x45\x01\x44\x01"
3762be45b66SKalle Valo "\x44\x01\x43\x01\x43\x01\x42\x01"
3772be45b66SKalle Valo "\x42\x01\x41\x01\x41\x01\x40\x01"
3782be45b66SKalle Valo "\x40\x01\x40\x01\x40\x01\x40\x01"
3792be45b66SKalle Valo "\x40\x01\x40\x01\x40\x01\x40\x01"
3802be45b66SKalle Valo "\x40\x01\x40\x01\x40\x01\x40\x01"
3812be45b66SKalle Valo "\x40\x01\x40\x01\x40\x01\x40\x01");
3822be45b66SKalle Valo
3832be45b66SKalle Valo /* Write PDA according to certain rules.
3842be45b66SKalle Valo *
3852be45b66SKalle Valo * For every production data record, look for a previous setting in
3862be45b66SKalle Valo * the pda, and use that.
3872be45b66SKalle Valo *
3882be45b66SKalle Valo * For certain records, use defaults if they are not found in pda.
3892be45b66SKalle Valo */
hermes_apply_pda_with_defaults(struct hermes * hw,const char * first_pdr,const void * pdr_end,const __le16 * pda,const void * pda_end)3902be45b66SKalle Valo int hermes_apply_pda_with_defaults(struct hermes *hw,
3912be45b66SKalle Valo const char *first_pdr,
3922be45b66SKalle Valo const void *pdr_end,
3932be45b66SKalle Valo const __le16 *pda,
3942be45b66SKalle Valo const void *pda_end)
3952be45b66SKalle Valo {
3962be45b66SKalle Valo const struct pdr *pdr = (const struct pdr *) first_pdr;
3972be45b66SKalle Valo const struct pdi *first_pdi = (const struct pdi *) &pda[2];
3982be45b66SKalle Valo const struct pdi *pdi;
3992be45b66SKalle Valo const struct pdi *default_pdi = NULL;
4002be45b66SKalle Valo const struct pdi *outdoor_pdi;
4012be45b66SKalle Valo int record_id;
4022be45b66SKalle Valo
4032be45b66SKalle Valo pdr_end -= sizeof(struct pdr);
4042be45b66SKalle Valo
4052be45b66SKalle Valo while (((void *) pdr <= pdr_end) &&
4062be45b66SKalle Valo (pdr_id(pdr) != PDI_END)) {
4072be45b66SKalle Valo /*
4082be45b66SKalle Valo * For spectrum_cs firmwares,
4092be45b66SKalle Valo * PDR area is currently not terminated by PDI_END.
4102be45b66SKalle Valo * It's followed by CRC records, which have the type
4112be45b66SKalle Valo * field where PDR has length. The type can be 0 or 1.
4122be45b66SKalle Valo */
4132be45b66SKalle Valo if (pdr_len(pdr) < 2)
4142be45b66SKalle Valo break;
4152be45b66SKalle Valo record_id = pdr_id(pdr);
4162be45b66SKalle Valo
4172be45b66SKalle Valo pdi = hermes_find_pdi(first_pdi, record_id, pda_end);
4182be45b66SKalle Valo if (pdi)
4192be45b66SKalle Valo pr_debug(PFX "Found record 0x%04x at %p\n",
4202be45b66SKalle Valo record_id, pdi);
4212be45b66SKalle Valo
4222be45b66SKalle Valo switch (record_id) {
4232be45b66SKalle Valo case 0x110: /* Modem REFDAC values */
4242be45b66SKalle Valo case 0x120: /* Modem VGDAC values */
4252be45b66SKalle Valo outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1,
4262be45b66SKalle Valo pda_end);
4272be45b66SKalle Valo default_pdi = NULL;
4282be45b66SKalle Valo if (outdoor_pdi) {
4292be45b66SKalle Valo pdi = outdoor_pdi;
4302be45b66SKalle Valo pr_debug(PFX
4312be45b66SKalle Valo "Using outdoor record 0x%04x at %p\n",
4322be45b66SKalle Valo record_id + 1, pdi);
4332be45b66SKalle Valo }
4342be45b66SKalle Valo break;
4352be45b66SKalle Valo case 0x5: /* HWIF Compatibility */
4362be45b66SKalle Valo default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
4372be45b66SKalle Valo break;
4382be45b66SKalle Valo case 0x108: /* PPPPSign */
4392be45b66SKalle Valo default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
4402be45b66SKalle Valo break;
4412be45b66SKalle Valo case 0x109: /* PPPPProf */
4422be45b66SKalle Valo default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
4432be45b66SKalle Valo break;
4442be45b66SKalle Valo case 0x150: /* Antenna diversity */
4452be45b66SKalle Valo default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
4462be45b66SKalle Valo break;
4472be45b66SKalle Valo case 0x160: /* Modem VCO band Set-up */
4482be45b66SKalle Valo default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
4492be45b66SKalle Valo break;
4502be45b66SKalle Valo case 0x161: /* Modem Rx Gain Table Values */
4512be45b66SKalle Valo default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
4522be45b66SKalle Valo break;
4532be45b66SKalle Valo default:
4542be45b66SKalle Valo default_pdi = NULL;
4552be45b66SKalle Valo break;
4562be45b66SKalle Valo }
4572be45b66SKalle Valo if (!pdi && default_pdi) {
4582be45b66SKalle Valo /* Use default */
4592be45b66SKalle Valo pdi = default_pdi;
4602be45b66SKalle Valo pr_debug(PFX "Using default record 0x%04x at %p\n",
4612be45b66SKalle Valo record_id, pdi);
4622be45b66SKalle Valo }
4632be45b66SKalle Valo
4642be45b66SKalle Valo if (pdi) {
4652be45b66SKalle Valo /* Lengths of the data in PDI and PDR must match */
4662be45b66SKalle Valo if ((pdi_len(pdi) == pdr_len(pdr)) &&
4672be45b66SKalle Valo ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
4682be45b66SKalle Valo /* do the actual plugging */
4692be45b66SKalle Valo hw->ops->program(hw, pdi->data, pdr_addr(pdr),
4702be45b66SKalle Valo pdi_len(pdi));
4712be45b66SKalle Valo }
4722be45b66SKalle Valo }
4732be45b66SKalle Valo
4742be45b66SKalle Valo pdr++;
4752be45b66SKalle Valo }
4762be45b66SKalle Valo return 0;
4772be45b66SKalle Valo }
478