summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/spectrum_cs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/spectrum_cs.c')
-rw-r--r--drivers/net/wireless/spectrum_cs.c428
1 files changed, 15 insertions, 413 deletions
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 98df9bc..67b26d3 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;
}
OpenPOWER on IntegriCloud