• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
64 struct 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  */
75 struct 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  */
87 struct 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 
95 static inline u32
dblock_addr(const struct dblock * blk)96 dblock_addr(const struct dblock *blk)
97 {
98 	return le32_to_cpu(blk->addr);
99 }
100 
101 static inline u32
dblock_len(const struct dblock * blk)102 dblock_len(const struct dblock *blk)
103 {
104 	return le16_to_cpu(blk->len);
105 }
106 
107 /*** PDR Access functions ***/
108 
109 static inline u32
pdr_id(const struct pdr * pdr)110 pdr_id(const struct pdr *pdr)
111 {
112 	return le32_to_cpu(pdr->id);
113 }
114 
115 static inline u32
pdr_addr(const struct pdr * pdr)116 pdr_addr(const struct pdr *pdr)
117 {
118 	return le32_to_cpu(pdr->addr);
119 }
120 
121 static inline u32
pdr_len(const struct pdr * pdr)122 pdr_len(const struct pdr *pdr)
123 {
124 	return le32_to_cpu(pdr->len);
125 }
126 
127 /*** PDI Access functions ***/
128 
129 static inline u32
pdi_id(const struct pdi * pdi)130 pdi_id(const struct pdi *pdi)
131 {
132 	return le16_to_cpu(pdi->id);
133 }
134 
135 /* Return length of the data only, in bytes */
136 static inline u32
pdi_len(const struct pdi * pdi)137 pdi_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  */
148 static const struct pdr *
hermes_find_pdr(const struct pdr * first_pdr,u32 record_id,const void * end)149 hermes_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 */
175 static const struct pdi *
hermes_find_pdi(const struct pdi * first_pdi,u32 record_id,const void * end)176 hermes_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 */
195 static int
hermes_plug_pdi(struct hermes * hw,const struct pdr * first_pdr,const struct pdi * pdi,const void * pdr_end)196 hermes_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  */
hermes_apply_pda(struct hermes * hw,const char * first_pdr,const void * pdr_end,const __le16 * pda,const void * pda_end)223 int 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  */
253 size_t
hermes_blocks_length(const char * first_block,const void * end)254 hermes_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 */
hermes_program(struct hermes * hw,const char * first_block,const void * end)277 int 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)				\
316 static 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 */
330 DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
331 
332 /* PPPPSign */
333 DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
334 
335 /* PPPPProf */
336 DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
337 
338 /* Antenna diversity */
339 DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
340 
341 /* Modem VCO band Set-up */
342 DEFINE_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 */
349 DEFINE_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  */
hermes_apply_pda_with_defaults(struct hermes * hw,const char * first_pdr,const void * pdr_end,const __le16 * pda,const void * pda_end)390 int 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 }
478