orinoco: Make firmware download logic more generic
[linux-2.6] / drivers / net / wireless / hermes_dld.c
1 /*
2  * Hermes download helper driver.
3  *
4  * This could be entirely merged into hermes.c.
5  *
6  * I'm keeping it separate to minimise the amount of merging between
7  * kernel upgrades. It also means the memory overhead for drivers that
8  * don't need firmware download low.
9  *
10  * This driver:
11  *  - is capable of writing to the volatile area of the hermes device
12  *  - is currently not capable of writing to non-volatile areas
13  *  - provide helpers to identify and update plugin data
14  *  - is not capable of interpreting a fw image directly. That is up to
15  *    the main card driver.
16  *  - deals with Hermes I devices. It can probably be modified to deal
17  *    with Hermes II devices
18  *
19  * Copyright (C) 2007, David Kilroy
20  *
21  * Plug data code slightly modified from spectrum_cs driver
22  *    Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
23  * Portions based on information in wl_lkm_718 Agere driver
24  *    COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
25  *
26  * The contents of this file are subject to the Mozilla Public License
27  * Version 1.1 (the "License"); you may not use this file except in
28  * compliance with the License. You may obtain a copy of the License
29  * at http://www.mozilla.org/MPL/
30  *
31  * Software distributed under the License is distributed on an "AS IS"
32  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
33  * the License for the specific language governing rights and
34  * limitations under the License.
35  *
36  * Alternatively, the contents of this file may be used under the
37  * terms of the GNU General Public License version 2 (the "GPL"), in
38  * which case the provisions of the GPL are applicable instead of the
39  * above.  If you wish to allow the use of your version of this file
40  * only under the terms of the GPL and not to allow others to use your
41  * version of this file under the MPL, indicate your decision by
42  * deleting the provisions above and replace them with the notice and
43  * other provisions required by the GPL.  If you do not delete the
44  * provisions above, a recipient may use your version of this file
45  * under either the MPL or the GPL.
46  */
47
48 #include <linux/module.h>
49 #include <linux/delay.h>
50 #include "hermes.h"
51 #include "hermes_dld.h"
52
53 MODULE_DESCRIPTION("Download helper for Lucent Hermes chipset");
54 MODULE_AUTHOR("David Kilroy <kilroyd@gmail.com>");
55 MODULE_LICENSE("Dual MPL/GPL");
56
57 #define PFX "hermes_dld: "
58
59 /*
60  * AUX port access.  To unlock the AUX port write the access keys to the
61  * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
62  * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
63  */
64 #define HERMES_AUX_ENABLE       0x8000  /* Enable auxiliary port access */
65 #define HERMES_AUX_DISABLE      0x4000  /* Disable to auxiliary port access */
66 #define HERMES_AUX_ENABLED      0xC000  /* Auxiliary port is open */
67 #define HERMES_AUX_DISABLED     0x0000  /* Auxiliary port is closed */
68
69 #define HERMES_AUX_PW0  0xFE01
70 #define HERMES_AUX_PW1  0xDC23
71 #define HERMES_AUX_PW2  0xBA45
72
73 /* End markers used in dblocks */
74 #define PDI_END         0x00000000      /* End of PDA */
75 #define BLOCK_END       0xFFFFFFFF      /* Last image block */
76 #define TEXT_END        0x1A            /* End of text header */
77
78 /*
79  * PDA == Production Data Area
80  *
81  * In principle, the max. size of the PDA is is 4096 words. Currently,
82  * however, only about 500 bytes of this area are used.
83  *
84  * Some USB implementations can't handle sizes in excess of 1016. Note
85  * that PDA is not actually used in those USB environments, but may be
86  * retrieved by common code.
87  */
88 #define MAX_PDA_SIZE    1000
89
90 /* Limit the amout we try to download in a single shot.
91  * Size is in bytes.
92  */
93 #define MAX_DL_SIZE 1024
94 #define LIMIT_PROGRAM_SIZE 0
95
96 /*
97  * The following structures have little-endian fields denoted by
98  * the leading underscore.  Don't access them directly - use inline
99  * functions defined below.
100  */
101
102 /*
103  * The binary image to be downloaded consists of series of data blocks.
104  * Each block has the following structure.
105  */
106 struct dblock {
107         __le32 addr;            /* adapter address where to write the block */
108         __le16 len;             /* length of the data only, in bytes */
109         char data[0];           /* data to be written */
110 } __attribute__ ((packed));
111
112 /*
113  * Plug Data References are located in in the image after the last data
114  * block.  They refer to areas in the adapter memory where the plug data
115  * items with matching ID should be written.
116  */
117 struct pdr {
118         __le32 id;              /* record ID */
119         __le32 addr;            /* adapter address where to write the data */
120         __le32 len;             /* expected length of the data, in bytes */
121         char next[0];           /* next PDR starts here */
122 } __attribute__ ((packed));
123
124 /*
125  * Plug Data Items are located in the EEPROM read from the adapter by
126  * primary firmware.  They refer to the device-specific data that should
127  * be plugged into the secondary firmware.
128  */
129 struct pdi {
130         __le16 len;             /* length of ID and data, in words */
131         __le16 id;              /* record ID */
132         char data[0];           /* plug data */
133 } __attribute__ ((packed));
134
135 /*** FW data block access functions ***/
136
137 static inline u32
138 dblock_addr(const struct dblock *blk)
139 {
140         return le32_to_cpu(blk->addr);
141 }
142
143 static inline u32
144 dblock_len(const struct dblock *blk)
145 {
146         return le16_to_cpu(blk->len);
147 }
148
149 /*** PDR Access functions ***/
150
151 static inline u32
152 pdr_id(const struct pdr *pdr)
153 {
154         return le32_to_cpu(pdr->id);
155 }
156
157 static inline u32
158 pdr_addr(const struct pdr *pdr)
159 {
160         return le32_to_cpu(pdr->addr);
161 }
162
163 static inline u32
164 pdr_len(const struct pdr *pdr)
165 {
166         return le32_to_cpu(pdr->len);
167 }
168
169 /*** PDI Access functions ***/
170
171 static inline u32
172 pdi_id(const struct pdi *pdi)
173 {
174         return le16_to_cpu(pdi->id);
175 }
176
177 /* Return length of the data only, in bytes */
178 static inline u32
179 pdi_len(const struct pdi *pdi)
180 {
181         return 2 * (le16_to_cpu(pdi->len) - 1);
182 }
183
184 /*** Hermes AUX control ***/
185
186 static inline void
187 hermes_aux_setaddr(hermes_t *hw, u32 addr)
188 {
189         hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
190         hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
191 }
192
193 static inline int
194 hermes_aux_control(hermes_t *hw, int enabled)
195 {
196         int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
197         int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
198         int i;
199
200         /* Already open? */
201         if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
202                 return 0;
203
204         hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
205         hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
206         hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
207         hermes_write_reg(hw, HERMES_CONTROL, action);
208
209         for (i = 0; i < 20; i++) {
210                 udelay(10);
211                 if (hermes_read_reg(hw, HERMES_CONTROL) ==
212                     desired_state)
213                         return 0;
214         }
215
216         return -EBUSY;
217 }
218
219 /*** Plug Data Functions ***/
220
221 /*
222  * Scan PDR for the record with the specified RECORD_ID.
223  * If it's not found, return NULL.
224  */
225 static struct pdr *
226 hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
227 {
228         struct pdr *pdr = first_pdr;
229         void *end = (void *)first_pdr + MAX_PDA_SIZE;
230
231         while (((void *)pdr < end) &&
232                (pdr_id(pdr) != PDI_END)) {
233                 /*
234                  * PDR area is currently not terminated by PDI_END.
235                  * It's followed by CRC records, which have the type
236                  * field where PDR has length.  The type can be 0 or 1.
237                  */
238                 if (pdr_len(pdr) < 2)
239                         return NULL;
240
241                 /* If the record ID matches, we are done */
242                 if (pdr_id(pdr) == record_id)
243                         return pdr;
244
245                 pdr = (struct pdr *) pdr->next;
246         }
247         return NULL;
248 }
249
250 /* Process one Plug Data Item - find corresponding PDR and plug it */
251 static int
252 hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
253 {
254         struct pdr *pdr;
255
256         /* Find the PDR corresponding to this PDI */
257         pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));
258
259         /* No match is found, safe to ignore */
260         if (!pdr)
261                 return 0;
262
263         /* Lengths of the data in PDI and PDR must match */
264         if (pdi_len(pdi) != pdr_len(pdr))
265                 return -EINVAL;
266
267         /* do the actual plugging */
268         hermes_aux_setaddr(hw, pdr_addr(pdr));
269         hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
270
271         return 0;
272 }
273
274 /* Read PDA from the adapter */
275 int hermes_read_pda(hermes_t *hw,
276                     __le16 *pda,
277                     u32 pda_addr,
278                     u16 pda_len,
279                     int use_eeprom) /* can we get this into hw? */
280 {
281         int ret;
282         u16 pda_size;
283         u16 data_len = pda_len;
284         __le16 *data = pda;
285
286         if (use_eeprom) {
287                 /* PDA of spectrum symbol is in eeprom */
288
289                 /* Issue command to read EEPROM */
290                 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
291                 if (ret)
292                         return ret;
293         }
294
295         /* Open auxiliary port */
296         ret = hermes_aux_control(hw, 1);
297         printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
298         if (ret)
299                 return ret;
300
301         /* read PDA from EEPROM */
302         hermes_aux_setaddr(hw, pda_addr);
303         hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
304
305         /* Close aux port */
306         ret = hermes_aux_control(hw, 0);
307         printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);
308
309         /* Check PDA length */
310         pda_size = le16_to_cpu(pda[0]);
311         printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
312                pda_size, pda_len);
313         if (pda_size > pda_len)
314                 return -EINVAL;
315
316         return 0;
317 }
318 EXPORT_SYMBOL(hermes_read_pda);
319
320 /* Parse PDA and write the records into the adapter
321  *
322  * Attempt to write every records that is in the specified pda
323  * which also has a valid production data record for the firmware.
324  */
325 int hermes_apply_pda(hermes_t *hw,
326                      const char *first_pdr,
327                      const __le16 *pda)
328 {
329         int ret;
330         const struct pdi *pdi;
331         struct pdr *pdr;
332
333         pdr = (struct pdr *) first_pdr;
334
335         /* Go through every PDI and plug them into the adapter */
336         pdi = (const struct pdi *) (pda + 2);
337         while (pdi_id(pdi) != PDI_END) {
338                 ret = hermes_plug_pdi(hw, pdr, pdi);
339                 if (ret)
340                         return ret;
341
342                 /* Increment to the next PDI */
343                 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
344         }
345         return 0;
346 }
347 EXPORT_SYMBOL(hermes_apply_pda);
348
349 /* Identify the total number of bytes in all blocks
350  * including the header data.
351  */
352 size_t
353 hermes_blocks_length(const char *first_block)
354 {
355         const struct dblock *blk = (const struct dblock *) first_block;
356         int total_len = 0;
357         int len;
358
359         /* Skip all blocks to locate Plug Data References
360          * (Spectrum CS) */
361         while (dblock_addr(blk) != BLOCK_END) {
362                 len = dblock_len(blk);
363                 total_len += sizeof(*blk) + len;
364                 blk = (struct dblock *) &blk->data[len];
365         }
366
367         return total_len;
368 }
369 EXPORT_SYMBOL(hermes_blocks_length);
370
371 /*** Hermes programming ***/
372
373 /* Program the data blocks */
374 int hermes_program(hermes_t *hw, const char *first_block, const char *end)
375 {
376         const struct dblock *blk;
377         u32 blkaddr;
378         u32 blklen;
379 #if LIMIT_PROGRAM_SIZE
380         u32 addr;
381         u32 len;
382 #endif
383
384         blk = (const struct dblock *) first_block;
385
386         if ((const char *) blk > (end - sizeof(*blk)))
387                 return -EIO;
388
389         blkaddr = dblock_addr(blk);
390         blklen = dblock_len(blk);
391
392         while ((blkaddr != BLOCK_END) &&
393                (((const char *) blk + blklen) <= end)) {
394                 printk(KERN_DEBUG PFX
395                        "Programming block of length %d to address 0x%08x\n",
396                        blklen, blkaddr);
397
398 #if !LIMIT_PROGRAM_SIZE
399                 /* wl_lkm driver splits this into writes of 2000 bytes */
400                 hermes_aux_setaddr(hw, blkaddr);
401                 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
402                                    blklen);
403 #else
404                 len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
405                 addr = blkaddr;
406
407                 while (addr < (blkaddr + blklen)) {
408                         printk(KERN_DEBUG PFX
409                                "Programming subblock of length %d "
410                                "to address 0x%08x. Data @ %p\n",
411                                len, addr, &blk->data[addr - blkaddr]);
412
413                         hermes_aux_setaddr(hw, addr);
414                         hermes_write_bytes(hw, HERMES_AUXDATA,
415                                            &blk->data[addr - blkaddr],
416                                            len);
417
418                         addr += len;
419                         len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
420                                 (blkaddr + blklen - addr) : MAX_DL_SIZE;
421                 }
422 #endif
423                 blk = (const struct dblock *) &blk->data[blklen];
424
425                 if ((const char *) blk > (end - sizeof(*blk)))
426                         return -EIO;
427
428                 blkaddr = dblock_addr(blk);
429                 blklen = dblock_len(blk);
430         }
431         return 0;
432 }
433 EXPORT_SYMBOL(hermes_program);
434
435 static int __init init_hermes_dld(void)
436 {
437         return 0;
438 }
439
440 static void __exit exit_hermes_dld(void)
441 {
442 }
443
444 module_init(init_hermes_dld);
445 module_exit(exit_hermes_dld);