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.c590
1 files changed, 84 insertions, 506 deletions
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 98df9bc7836a..b0c71c3be467 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;
@@ -633,6 +235,70 @@ static void spectrum_cs_detach(struct pcmcia_device *link)
* device available to the system.
*/
+static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ goto next_entry;
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ goto next_entry;
+ }
+ return 0;
+
+next_entry:
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+};
+
static int
spectrum_cs_config(struct pcmcia_device *link)
{
@@ -641,16 +307,8 @@ spectrum_cs_config(struct pcmcia_device *link)
struct orinoco_pccard *card = priv->card;
hermes_t *hw = &priv->hw;
int last_fn, last_ret;
- u_char buf[64];
- config_info_t conf;
- tuple_t tuple;
- cisparse_t parse;
void __iomem *mem;
- /* Look up the current Vcc */
- CS_CHECK(GetConfigurationInfo,
- pcmcia_get_configuration_info(link, &conf));
-
/*
* In this loop, we scan the CIS for configuration table
* entries, each of which describes a valid card
@@ -665,94 +323,14 @@ spectrum_cs_config(struct pcmcia_device *link)
* and most client drivers will only use the CIS to fill in
* implementation-defined details.
*/
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- cistpl_cftable_entry_t dflt = { .index = 0 };
-
- if ( (pcmcia_get_tuple_data(link, &tuple) != 0)
- || (pcmcia_parse_tuple(link, &tuple, &parse) != 0))
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
- if(!ignore_cis_vcc)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- /* Do we need to allocate an interrupt? */
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io =
- (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 =
- IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 =
- IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines =
- io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 =
- link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
-
-
- /* If we got this far, we're cool! */
-
- break;
-
- next_entry:
- pcmcia_disable_device(link);
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret == CS_NO_MORE_ITEMS) {
+ last_ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
+ if (last_ret) {
+ if (!ignore_cis_vcc)
printk(KERN_ERR PFX "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
- goto cs_failed;
- }
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
/*
@@ -784,7 +362,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;
}