diff options
Diffstat (limited to 'drivers/net/wireless/spectrum_cs.c')
-rw-r--r-- | drivers/net/wireless/spectrum_cs.c | 428 |
1 files changed, 15 insertions, 413 deletions
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 98df9bc7836a..67b26d3c3cd5 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/firmware.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/cistpl.h> @@ -34,9 +33,6 @@ #include "orinoco.h" -static const char primary_fw_name[] = "symbol_sp24t_prim_fw"; -static const char secondary_fw_name[] = "symbol_sp24t_sec_fw"; - /********************************************************************/ /* Module stuff */ /********************************************************************/ @@ -71,161 +67,11 @@ struct orinoco_pccard { static int spectrum_cs_config(struct pcmcia_device *link); static void spectrum_cs_release(struct pcmcia_device *link); -/********************************************************************/ -/* Firmware downloader */ -/********************************************************************/ - -/* Position of PDA in the adapter memory */ -#define EEPROM_ADDR 0x3000 -#define EEPROM_LEN 0x200 -#define PDA_OFFSET 0x100 - -#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) -#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) - /* Constants for the CISREG_CCSR register */ #define HCR_RUN 0x07 /* run firmware after reset */ #define HCR_IDLE 0x0E /* don't run firmware after reset */ #define HCR_MEM16 0x10 /* memory width bit, should be preserved */ -/* - * AUX port access. To unlock the AUX port write the access keys to the - * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL - * register. Then read it and make sure it's HERMES_AUX_ENABLED. - */ -#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ -#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ -#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ - -#define HERMES_AUX_PW0 0xFE01 -#define HERMES_AUX_PW1 0xDC23 -#define HERMES_AUX_PW2 0xBA45 - -/* End markers */ -#define PDI_END 0x00000000 /* End of PDA */ -#define BLOCK_END 0xFFFFFFFF /* Last image block */ -#define TEXT_END 0x1A /* End of text header */ - -/* - * The following structures have little-endian fields denoted by - * the leading underscore. Don't access them directly - use inline - * functions defined below. - */ - -/* - * The binary image to be downloaded consists of series of data blocks. - * Each block has the following structure. - */ -struct dblock { - __le32 addr; /* adapter address where to write the block */ - __le16 len; /* length of the data only, in bytes */ - char data[0]; /* data to be written */ -} __attribute__ ((packed)); - -/* - * Plug Data References are located in in the image after the last data - * block. They refer to areas in the adapter memory where the plug data - * items with matching ID should be written. - */ -struct pdr { - __le32 id; /* record ID */ - __le32 addr; /* adapter address where to write the data */ - __le32 len; /* expected length of the data, in bytes */ - char next[0]; /* next PDR starts here */ -} __attribute__ ((packed)); - - -/* - * Plug Data Items are located in the EEPROM read from the adapter by - * primary firmware. They refer to the device-specific data that should - * be plugged into the secondary firmware. - */ -struct pdi { - __le16 len; /* length of ID and data, in words */ - __le16 id; /* record ID */ - char data[0]; /* plug data */ -} __attribute__ ((packed)); - - -/* Functions for access to little-endian data */ -static inline u32 -dblock_addr(const struct dblock *blk) -{ - return le32_to_cpu(blk->addr); -} - -static inline u32 -dblock_len(const struct dblock *blk) -{ - return le16_to_cpu(blk->len); -} - -static inline u32 -pdr_id(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->id); -} - -static inline u32 -pdr_addr(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->addr); -} - -static inline u32 -pdr_len(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->len); -} - -static inline u32 -pdi_id(const struct pdi *pdi) -{ - return le16_to_cpu(pdi->id); -} - -/* Return length of the data only, in bytes */ -static inline u32 -pdi_len(const struct pdi *pdi) -{ - return 2 * (le16_to_cpu(pdi->len) - 1); -} - - -/* Set address of the auxiliary port */ -static inline void -spectrum_aux_setaddr(hermes_t *hw, u32 addr) -{ - hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); - hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); -} - - -/* Open access to the auxiliary port */ -static int -spectrum_aux_open(hermes_t *hw) -{ - int i; - - /* Already open? */ - if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) - return 0; - - hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); - hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); - hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); - hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); - - for (i = 0; i < 20; i++) { - udelay(10); - if (hermes_read_reg(hw, HERMES_CONTROL) == - HERMES_AUX_ENABLED) - return 0; - } - - return -EBUSY; -} - #define CS_CHECK(fn, ret) \ 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) return -ENODEV; } +/********************************************************************/ +/* Device methods */ +/********************************************************************/ -/* - * Scan PDR for the record with the specified RECORD_ID. - * If it's not found, return NULL. - */ -static struct pdr * -spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) -{ - struct pdr *pdr = first_pdr; - - while (pdr_id(pdr) != PDI_END) { - /* - * PDR area is currently not terminated by PDI_END. - * It's followed by CRC records, which have the type - * field where PDR has length. The type can be 0 or 1. - */ - if (pdr_len(pdr) < 2) - return NULL; - - /* If the record ID matches, we are done */ - if (pdr_id(pdr) == record_id) - return pdr; - - pdr = (struct pdr *) pdr->next; - } - return NULL; -} - - -/* Process one Plug Data Item - find corresponding PDR and plug it */ -static int -spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) -{ - struct pdr *pdr; - - /* Find the PDI corresponding to this PDR */ - pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); - - /* No match is found, safe to ignore */ - if (!pdr) - return 0; - - /* Lengths of the data in PDI and PDR must match */ - if (pdi_len(pdi) != pdr_len(pdr)) - return -EINVAL; - - /* do the actual plugging */ - spectrum_aux_setaddr(hw, pdr_addr(pdr)); - hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); - - return 0; -} - - -/* Read PDA from the adapter */ -static int -spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) -{ - int ret; - int pda_size; - - /* Issue command to read EEPROM */ - ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); - if (ret) - return ret; - - /* Open auxiliary port */ - ret = spectrum_aux_open(hw); - if (ret) - return ret; - - /* read PDA from EEPROM */ - spectrum_aux_setaddr(hw, PDA_ADDR); - hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); - - /* Check PDA length */ - pda_size = le16_to_cpu(pda[0]); - if (pda_size > pda_len) - return -EINVAL; - - return 0; -} - - -/* Parse PDA and write the records into the adapter */ -static int -spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, - __le16 *pda) -{ - int ret; - struct pdi *pdi; - struct pdr *first_pdr; - const struct dblock *blk = first_block; - - /* Skip all blocks to locate Plug Data References */ - while (dblock_addr(blk) != BLOCK_END) - blk = (struct dblock *) &blk->data[dblock_len(blk)]; - - first_pdr = (struct pdr *) blk; - - /* Go through every PDI and plug them into the adapter */ - pdi = (struct pdi *) (pda + 2); - while (pdi_id(pdi) != PDI_END) { - ret = spectrum_plug_pdi(hw, first_pdr, pdi); - if (ret) - return ret; - - /* Increment to the next PDI */ - pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; - } - return 0; -} - - -/* Load firmware blocks into the adapter */ -static int -spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) -{ - const struct dblock *blk; - u32 blkaddr; - u32 blklen; - - blk = first_block; - blkaddr = dblock_addr(blk); - blklen = dblock_len(blk); - - while (dblock_addr(blk) != BLOCK_END) { - spectrum_aux_setaddr(hw, blkaddr); - hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, - blklen); - - blk = (struct dblock *) &blk->data[blklen]; - blkaddr = dblock_addr(blk); - blklen = dblock_len(blk); - } - return 0; -} - - -/* - * Process a firmware image - stop the card, load the firmware, reset - * the card and make sure it responds. For the secondary firmware take - * care of the PDA - read it and then write it on top of the firmware. - */ static int -spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, - const unsigned char *image, int secondary) +spectrum_cs_hard_reset(struct orinoco_private *priv) { - int ret; - const unsigned char *ptr; - const struct dblock *first_block; - - /* Plug Data Area (PDA) */ - __le16 pda[PDA_WORDS]; - - /* Binary block begins after the 0x1A marker */ - ptr = image; - while (*ptr++ != TEXT_END); - first_block = (const struct dblock *) ptr; - - /* Read the PDA */ - if (secondary) { - ret = spectrum_read_pda(hw, pda, sizeof(pda)); - if (ret) - return ret; - } - - /* Stop the firmware, so that it can be safely rewritten */ - ret = spectrum_reset(link, 1); - if (ret) - return ret; - - /* Program the adapter with new firmware */ - ret = spectrum_load_blocks(hw, first_block); - if (ret) - return ret; - - /* Write the PDA to the adapter */ - if (secondary) { - ret = spectrum_apply_pda(hw, first_block, pda); - if (ret) - return ret; - } - - /* Run the firmware */ - ret = spectrum_reset(link, 0); - if (ret) - return ret; - - /* Reset hermes chip and make sure it responds */ - ret = hermes_init(hw); - - /* hermes_reset() should return 0 with the secondary firmware */ - if (secondary && ret != 0) - return -ENODEV; + struct orinoco_pccard *card = priv->card; + struct pcmcia_device *link = card->p_dev; - /* And this should work with any firmware */ - if (!hermes_present(hw)) - return -ENODEV; + /* Soft reset using COR and HCR */ + spectrum_reset(link, 0); return 0; } - -/* - * Download the firmware into the card, this also does a PCMCIA soft - * reset on the card, to make sure it's in a sane state. - */ static int -spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) -{ - int ret; - const struct firmware *fw_entry; - - if (request_firmware(&fw_entry, primary_fw_name, - &handle_to_dev(link)) != 0) { - printk(KERN_ERR PFX "Cannot find firmware: %s\n", - primary_fw_name); - return -ENOENT; - } - - /* Load primary firmware */ - ret = spectrum_dl_image(hw, link, fw_entry->data, 0); - release_firmware(fw_entry); - if (ret) { - printk(KERN_ERR PFX "Primary firmware download failed\n"); - return ret; - } - - if (request_firmware(&fw_entry, secondary_fw_name, - &handle_to_dev(link)) != 0) { - printk(KERN_ERR PFX "Cannot find firmware: %s\n", - secondary_fw_name); - return -ENOENT; - } - - /* Load secondary firmware */ - ret = spectrum_dl_image(hw, link, fw_entry->data, 1); - release_firmware(fw_entry); - if (ret) { - printk(KERN_ERR PFX "Secondary firmware download failed\n"); - } - - return ret; -} - -/********************************************************************/ -/* Device methods */ -/********************************************************************/ - -static int -spectrum_cs_hard_reset(struct orinoco_private *priv) +spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) { struct orinoco_pccard *card = priv->card; struct pcmcia_device *link = card->p_dev; - int err; - if (!hermes_present(&priv->hw)) { - /* The firmware needs to be reloaded */ - if (spectrum_dl_firmware(&priv->hw, link) != 0) { - printk(KERN_ERR PFX "Firmware download failed\n"); - err = -ENODEV; - } - } else { - /* Soft reset using COR and HCR */ - spectrum_reset(link, 0); - } - - return 0; + return spectrum_reset(link, idle); } /********************************************************************/ @@ -582,7 +182,9 @@ spectrum_cs_probe(struct pcmcia_device *link) struct orinoco_private *priv; struct orinoco_pccard *card; - dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset); + dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), + spectrum_cs_hard_reset, + spectrum_cs_stop_firmware); if (! dev) return -ENOMEM; priv = netdev_priv(dev); @@ -593,7 +195,7 @@ spectrum_cs_probe(struct pcmcia_device *link) link->priv = dev; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = orinoco_interrupt; link->irq.Instance = dev; @@ -784,7 +386,7 @@ spectrum_cs_config(struct pcmcia_device *link) dev->irq = link->irq.AssignedIRQ; card->node.major = card->node.minor = 0; - /* Reset card and download firmware */ + /* Reset card */ if (spectrum_cs_hard_reset(priv) != 0) { goto failed; } |