diff options
Diffstat (limited to 'drivers/net/wireless/spectrum_cs.c')
-rw-r--r-- | drivers/net/wireless/spectrum_cs.c | 426 |
1 files changed, 14 insertions, 412 deletions
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 98df9bc7836a..e368759d1d89 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
27 | #include <linux/delay.h> | 27 | #include <linux/delay.h> |
28 | #include <linux/firmware.h> | ||
29 | #include <pcmcia/cs_types.h> | 28 | #include <pcmcia/cs_types.h> |
30 | #include <pcmcia/cs.h> | 29 | #include <pcmcia/cs.h> |
31 | #include <pcmcia/cistpl.h> | 30 | #include <pcmcia/cistpl.h> |
@@ -34,9 +33,6 @@ | |||
34 | 33 | ||
35 | #include "orinoco.h" | 34 | #include "orinoco.h" |
36 | 35 | ||
37 | static const char primary_fw_name[] = "symbol_sp24t_prim_fw"; | ||
38 | static const char secondary_fw_name[] = "symbol_sp24t_sec_fw"; | ||
39 | |||
40 | /********************************************************************/ | 36 | /********************************************************************/ |
41 | /* Module stuff */ | 37 | /* Module stuff */ |
42 | /********************************************************************/ | 38 | /********************************************************************/ |
@@ -71,161 +67,11 @@ struct orinoco_pccard { | |||
71 | static int spectrum_cs_config(struct pcmcia_device *link); | 67 | static int spectrum_cs_config(struct pcmcia_device *link); |
72 | static void spectrum_cs_release(struct pcmcia_device *link); | 68 | static void spectrum_cs_release(struct pcmcia_device *link); |
73 | 69 | ||
74 | /********************************************************************/ | ||
75 | /* Firmware downloader */ | ||
76 | /********************************************************************/ | ||
77 | |||
78 | /* Position of PDA in the adapter memory */ | ||
79 | #define EEPROM_ADDR 0x3000 | ||
80 | #define EEPROM_LEN 0x200 | ||
81 | #define PDA_OFFSET 0x100 | ||
82 | |||
83 | #define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) | ||
84 | #define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) | ||
85 | |||
86 | /* Constants for the CISREG_CCSR register */ | 70 | /* Constants for the CISREG_CCSR register */ |
87 | #define HCR_RUN 0x07 /* run firmware after reset */ | 71 | #define HCR_RUN 0x07 /* run firmware after reset */ |
88 | #define HCR_IDLE 0x0E /* don't run firmware after reset */ | 72 | #define HCR_IDLE 0x0E /* don't run firmware after reset */ |
89 | #define HCR_MEM16 0x10 /* memory width bit, should be preserved */ | 73 | #define HCR_MEM16 0x10 /* memory width bit, should be preserved */ |
90 | 74 | ||
91 | /* | ||
92 | * AUX port access. To unlock the AUX port write the access keys to the | ||
93 | * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL | ||
94 | * register. Then read it and make sure it's HERMES_AUX_ENABLED. | ||
95 | */ | ||
96 | #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ | ||
97 | #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ | ||
98 | #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ | ||
99 | |||
100 | #define HERMES_AUX_PW0 0xFE01 | ||
101 | #define HERMES_AUX_PW1 0xDC23 | ||
102 | #define HERMES_AUX_PW2 0xBA45 | ||
103 | |||
104 | /* End markers */ | ||
105 | #define PDI_END 0x00000000 /* End of PDA */ | ||
106 | #define BLOCK_END 0xFFFFFFFF /* Last image block */ | ||
107 | #define TEXT_END 0x1A /* End of text header */ | ||
108 | |||
109 | /* | ||
110 | * The following structures have little-endian fields denoted by | ||
111 | * the leading underscore. Don't access them directly - use inline | ||
112 | * functions defined below. | ||
113 | */ | ||
114 | |||
115 | /* | ||
116 | * The binary image to be downloaded consists of series of data blocks. | ||
117 | * Each block has the following structure. | ||
118 | */ | ||
119 | struct dblock { | ||
120 | __le32 addr; /* adapter address where to write the block */ | ||
121 | __le16 len; /* length of the data only, in bytes */ | ||
122 | char data[0]; /* data to be written */ | ||
123 | } __attribute__ ((packed)); | ||
124 | |||
125 | /* | ||
126 | * Plug Data References are located in in the image after the last data | ||
127 | * block. They refer to areas in the adapter memory where the plug data | ||
128 | * items with matching ID should be written. | ||
129 | */ | ||
130 | struct pdr { | ||
131 | __le32 id; /* record ID */ | ||
132 | __le32 addr; /* adapter address where to write the data */ | ||
133 | __le32 len; /* expected length of the data, in bytes */ | ||
134 | char next[0]; /* next PDR starts here */ | ||
135 | } __attribute__ ((packed)); | ||
136 | |||
137 | |||
138 | /* | ||
139 | * Plug Data Items are located in the EEPROM read from the adapter by | ||
140 | * primary firmware. They refer to the device-specific data that should | ||
141 | * be plugged into the secondary firmware. | ||
142 | */ | ||
143 | struct pdi { | ||
144 | __le16 len; /* length of ID and data, in words */ | ||
145 | __le16 id; /* record ID */ | ||
146 | char data[0]; /* plug data */ | ||
147 | } __attribute__ ((packed)); | ||
148 | |||
149 | |||
150 | /* Functions for access to little-endian data */ | ||
151 | static inline u32 | ||
152 | dblock_addr(const struct dblock *blk) | ||
153 | { | ||
154 | return le32_to_cpu(blk->addr); | ||
155 | } | ||
156 | |||
157 | static inline u32 | ||
158 | dblock_len(const struct dblock *blk) | ||
159 | { | ||
160 | return le16_to_cpu(blk->len); | ||
161 | } | ||
162 | |||
163 | static inline u32 | ||
164 | pdr_id(const struct pdr *pdr) | ||
165 | { | ||
166 | return le32_to_cpu(pdr->id); | ||
167 | } | ||
168 | |||
169 | static inline u32 | ||
170 | pdr_addr(const struct pdr *pdr) | ||
171 | { | ||
172 | return le32_to_cpu(pdr->addr); | ||
173 | } | ||
174 | |||
175 | static inline u32 | ||
176 | pdr_len(const struct pdr *pdr) | ||
177 | { | ||
178 | return le32_to_cpu(pdr->len); | ||
179 | } | ||
180 | |||
181 | static inline u32 | ||
182 | pdi_id(const struct pdi *pdi) | ||
183 | { | ||
184 | return le16_to_cpu(pdi->id); | ||
185 | } | ||
186 | |||
187 | /* Return length of the data only, in bytes */ | ||
188 | static inline u32 | ||
189 | pdi_len(const struct pdi *pdi) | ||
190 | { | ||
191 | return 2 * (le16_to_cpu(pdi->len) - 1); | ||
192 | } | ||
193 | |||
194 | |||
195 | /* Set address of the auxiliary port */ | ||
196 | static inline void | ||
197 | spectrum_aux_setaddr(hermes_t *hw, u32 addr) | ||
198 | { | ||
199 | hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); | ||
200 | hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); | ||
201 | } | ||
202 | |||
203 | |||
204 | /* Open access to the auxiliary port */ | ||
205 | static int | ||
206 | spectrum_aux_open(hermes_t *hw) | ||
207 | { | ||
208 | int i; | ||
209 | |||
210 | /* Already open? */ | ||
211 | if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) | ||
212 | return 0; | ||
213 | |||
214 | hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); | ||
215 | hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); | ||
216 | hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); | ||
217 | hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); | ||
218 | |||
219 | for (i = 0; i < 20; i++) { | ||
220 | udelay(10); | ||
221 | if (hermes_read_reg(hw, HERMES_CONTROL) == | ||
222 | HERMES_AUX_ENABLED) | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | return -EBUSY; | ||
227 | } | ||
228 | |||
229 | 75 | ||
230 | #define CS_CHECK(fn, ret) \ | 76 | #define CS_CHECK(fn, ret) \ |
231 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | 77 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) |
@@ -292,275 +138,29 @@ spectrum_reset(struct pcmcia_device *link, int idle) | |||
292 | return -ENODEV; | 138 | return -ENODEV; |
293 | } | 139 | } |
294 | 140 | ||
141 | /********************************************************************/ | ||
142 | /* Device methods */ | ||
143 | /********************************************************************/ | ||
295 | 144 | ||
296 | /* | ||
297 | * Scan PDR for the record with the specified RECORD_ID. | ||
298 | * If it's not found, return NULL. | ||
299 | */ | ||
300 | static struct pdr * | ||
301 | spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) | ||
302 | { | ||
303 | struct pdr *pdr = first_pdr; | ||
304 | |||
305 | while (pdr_id(pdr) != PDI_END) { | ||
306 | /* | ||
307 | * PDR area is currently not terminated by PDI_END. | ||
308 | * It's followed by CRC records, which have the type | ||
309 | * field where PDR has length. The type can be 0 or 1. | ||
310 | */ | ||
311 | if (pdr_len(pdr) < 2) | ||
312 | return NULL; | ||
313 | |||
314 | /* If the record ID matches, we are done */ | ||
315 | if (pdr_id(pdr) == record_id) | ||
316 | return pdr; | ||
317 | |||
318 | pdr = (struct pdr *) pdr->next; | ||
319 | } | ||
320 | return NULL; | ||
321 | } | ||
322 | |||
323 | |||
324 | /* Process one Plug Data Item - find corresponding PDR and plug it */ | ||
325 | static int | ||
326 | spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) | ||
327 | { | ||
328 | struct pdr *pdr; | ||
329 | |||
330 | /* Find the PDI corresponding to this PDR */ | ||
331 | pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); | ||
332 | |||
333 | /* No match is found, safe to ignore */ | ||
334 | if (!pdr) | ||
335 | return 0; | ||
336 | |||
337 | /* Lengths of the data in PDI and PDR must match */ | ||
338 | if (pdi_len(pdi) != pdr_len(pdr)) | ||
339 | return -EINVAL; | ||
340 | |||
341 | /* do the actual plugging */ | ||
342 | spectrum_aux_setaddr(hw, pdr_addr(pdr)); | ||
343 | hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); | ||
344 | |||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | |||
349 | /* Read PDA from the adapter */ | ||
350 | static int | ||
351 | spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) | ||
352 | { | ||
353 | int ret; | ||
354 | int pda_size; | ||
355 | |||
356 | /* Issue command to read EEPROM */ | ||
357 | ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); | ||
358 | if (ret) | ||
359 | return ret; | ||
360 | |||
361 | /* Open auxiliary port */ | ||
362 | ret = spectrum_aux_open(hw); | ||
363 | if (ret) | ||
364 | return ret; | ||
365 | |||
366 | /* read PDA from EEPROM */ | ||
367 | spectrum_aux_setaddr(hw, PDA_ADDR); | ||
368 | hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); | ||
369 | |||
370 | /* Check PDA length */ | ||
371 | pda_size = le16_to_cpu(pda[0]); | ||
372 | if (pda_size > pda_len) | ||
373 | return -EINVAL; | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | |||
379 | /* Parse PDA and write the records into the adapter */ | ||
380 | static int | ||
381 | spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, | ||
382 | __le16 *pda) | ||
383 | { | ||
384 | int ret; | ||
385 | struct pdi *pdi; | ||
386 | struct pdr *first_pdr; | ||
387 | const struct dblock *blk = first_block; | ||
388 | |||
389 | /* Skip all blocks to locate Plug Data References */ | ||
390 | while (dblock_addr(blk) != BLOCK_END) | ||
391 | blk = (struct dblock *) &blk->data[dblock_len(blk)]; | ||
392 | |||
393 | first_pdr = (struct pdr *) blk; | ||
394 | |||
395 | /* Go through every PDI and plug them into the adapter */ | ||
396 | pdi = (struct pdi *) (pda + 2); | ||
397 | while (pdi_id(pdi) != PDI_END) { | ||
398 | ret = spectrum_plug_pdi(hw, first_pdr, pdi); | ||
399 | if (ret) | ||
400 | return ret; | ||
401 | |||
402 | /* Increment to the next PDI */ | ||
403 | pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | |||
409 | /* Load firmware blocks into the adapter */ | ||
410 | static int | ||
411 | spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) | ||
412 | { | ||
413 | const struct dblock *blk; | ||
414 | u32 blkaddr; | ||
415 | u32 blklen; | ||
416 | |||
417 | blk = first_block; | ||
418 | blkaddr = dblock_addr(blk); | ||
419 | blklen = dblock_len(blk); | ||
420 | |||
421 | while (dblock_addr(blk) != BLOCK_END) { | ||
422 | spectrum_aux_setaddr(hw, blkaddr); | ||
423 | hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, | ||
424 | blklen); | ||
425 | |||
426 | blk = (struct dblock *) &blk->data[blklen]; | ||
427 | blkaddr = dblock_addr(blk); | ||
428 | blklen = dblock_len(blk); | ||
429 | } | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | |||
434 | /* | ||
435 | * Process a firmware image - stop the card, load the firmware, reset | ||
436 | * the card and make sure it responds. For the secondary firmware take | ||
437 | * care of the PDA - read it and then write it on top of the firmware. | ||
438 | */ | ||
439 | static int | 145 | static int |
440 | spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, | 146 | spectrum_cs_hard_reset(struct orinoco_private *priv) |
441 | const unsigned char *image, int secondary) | ||
442 | { | 147 | { |
443 | int ret; | 148 | struct orinoco_pccard *card = priv->card; |
444 | const unsigned char *ptr; | 149 | struct pcmcia_device *link = card->p_dev; |
445 | const struct dblock *first_block; | ||
446 | |||
447 | /* Plug Data Area (PDA) */ | ||
448 | __le16 pda[PDA_WORDS]; | ||
449 | |||
450 | /* Binary block begins after the 0x1A marker */ | ||
451 | ptr = image; | ||
452 | while (*ptr++ != TEXT_END); | ||
453 | first_block = (const struct dblock *) ptr; | ||
454 | |||
455 | /* Read the PDA */ | ||
456 | if (secondary) { | ||
457 | ret = spectrum_read_pda(hw, pda, sizeof(pda)); | ||
458 | if (ret) | ||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | /* Stop the firmware, so that it can be safely rewritten */ | ||
463 | ret = spectrum_reset(link, 1); | ||
464 | if (ret) | ||
465 | return ret; | ||
466 | |||
467 | /* Program the adapter with new firmware */ | ||
468 | ret = spectrum_load_blocks(hw, first_block); | ||
469 | if (ret) | ||
470 | return ret; | ||
471 | |||
472 | /* Write the PDA to the adapter */ | ||
473 | if (secondary) { | ||
474 | ret = spectrum_apply_pda(hw, first_block, pda); | ||
475 | if (ret) | ||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | /* Run the firmware */ | ||
480 | ret = spectrum_reset(link, 0); | ||
481 | if (ret) | ||
482 | return ret; | ||
483 | |||
484 | /* Reset hermes chip and make sure it responds */ | ||
485 | ret = hermes_init(hw); | ||
486 | |||
487 | /* hermes_reset() should return 0 with the secondary firmware */ | ||
488 | if (secondary && ret != 0) | ||
489 | return -ENODEV; | ||
490 | 150 | ||
491 | /* And this should work with any firmware */ | 151 | /* Soft reset using COR and HCR */ |
492 | if (!hermes_present(hw)) | 152 | spectrum_reset(link, 0); |
493 | return -ENODEV; | ||
494 | 153 | ||
495 | return 0; | 154 | return 0; |
496 | } | 155 | } |
497 | 156 | ||
498 | |||
499 | /* | ||
500 | * Download the firmware into the card, this also does a PCMCIA soft | ||
501 | * reset on the card, to make sure it's in a sane state. | ||
502 | */ | ||
503 | static int | 157 | static int |
504 | spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) | 158 | spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) |
505 | { | ||
506 | int ret; | ||
507 | const struct firmware *fw_entry; | ||
508 | |||
509 | if (request_firmware(&fw_entry, primary_fw_name, | ||
510 | &handle_to_dev(link)) != 0) { | ||
511 | printk(KERN_ERR PFX "Cannot find firmware: %s\n", | ||
512 | primary_fw_name); | ||
513 | return -ENOENT; | ||
514 | } | ||
515 | |||
516 | /* Load primary firmware */ | ||
517 | ret = spectrum_dl_image(hw, link, fw_entry->data, 0); | ||
518 | release_firmware(fw_entry); | ||
519 | if (ret) { | ||
520 | printk(KERN_ERR PFX "Primary firmware download failed\n"); | ||
521 | return ret; | ||
522 | } | ||
523 | |||
524 | if (request_firmware(&fw_entry, secondary_fw_name, | ||
525 | &handle_to_dev(link)) != 0) { | ||
526 | printk(KERN_ERR PFX "Cannot find firmware: %s\n", | ||
527 | secondary_fw_name); | ||
528 | return -ENOENT; | ||
529 | } | ||
530 | |||
531 | /* Load secondary firmware */ | ||
532 | ret = spectrum_dl_image(hw, link, fw_entry->data, 1); | ||
533 | release_firmware(fw_entry); | ||
534 | if (ret) { | ||
535 | printk(KERN_ERR PFX "Secondary firmware download failed\n"); | ||
536 | } | ||
537 | |||
538 | return ret; | ||
539 | } | ||
540 | |||
541 | /********************************************************************/ | ||
542 | /* Device methods */ | ||
543 | /********************************************************************/ | ||
544 | |||
545 | static int | ||
546 | spectrum_cs_hard_reset(struct orinoco_private *priv) | ||
547 | { | 159 | { |
548 | struct orinoco_pccard *card = priv->card; | 160 | struct orinoco_pccard *card = priv->card; |
549 | struct pcmcia_device *link = card->p_dev; | 161 | struct pcmcia_device *link = card->p_dev; |
550 | int err; | ||
551 | 162 | ||
552 | if (!hermes_present(&priv->hw)) { | 163 | return spectrum_reset(link, idle); |
553 | /* The firmware needs to be reloaded */ | ||
554 | if (spectrum_dl_firmware(&priv->hw, link) != 0) { | ||
555 | printk(KERN_ERR PFX "Firmware download failed\n"); | ||
556 | err = -ENODEV; | ||
557 | } | ||
558 | } else { | ||
559 | /* Soft reset using COR and HCR */ | ||
560 | spectrum_reset(link, 0); | ||
561 | } | ||
562 | |||
563 | return 0; | ||
564 | } | 164 | } |
565 | 165 | ||
566 | /********************************************************************/ | 166 | /********************************************************************/ |
@@ -582,7 +182,9 @@ spectrum_cs_probe(struct pcmcia_device *link) | |||
582 | struct orinoco_private *priv; | 182 | struct orinoco_private *priv; |
583 | struct orinoco_pccard *card; | 183 | struct orinoco_pccard *card; |
584 | 184 | ||
585 | dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset); | 185 | dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), |
186 | spectrum_cs_hard_reset, | ||
187 | spectrum_cs_stop_firmware); | ||
586 | if (! dev) | 188 | if (! dev) |
587 | return -ENOMEM; | 189 | return -ENOMEM; |
588 | priv = netdev_priv(dev); | 190 | priv = netdev_priv(dev); |
@@ -784,7 +386,7 @@ spectrum_cs_config(struct pcmcia_device *link) | |||
784 | dev->irq = link->irq.AssignedIRQ; | 386 | dev->irq = link->irq.AssignedIRQ; |
785 | card->node.major = card->node.minor = 0; | 387 | card->node.major = card->node.minor = 0; |
786 | 388 | ||
787 | /* Reset card and download firmware */ | 389 | /* Reset card */ |
788 | if (spectrum_cs_hard_reset(priv) != 0) { | 390 | if (spectrum_cs_hard_reset(priv) != 0) { |
789 | goto failed; | 391 | goto failed; |
790 | } | 392 | } |