orinoco_usb: implement fw download
This involves some refactorring of the common fw download code to substitute ezusb versions of various functions. Note that WPA-enabled firmwares (9.xx series) will not work fully with orinoco_usb yet. Signed-off-by: David Kilroy <kilroyd@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
fc97431a50
commit
07cefe7ac9
@ -121,7 +121,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
|
||||
dev_dbg(dev, "Attempting to download firmware %s\n", firmware);
|
||||
|
||||
/* Read current plug data */
|
||||
err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0);
|
||||
err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
|
||||
dev_dbg(dev, "Read PDA returned %d\n", err);
|
||||
if (err)
|
||||
goto free;
|
||||
@ -148,7 +148,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
|
||||
}
|
||||
|
||||
/* Enable aux port to allow programming */
|
||||
err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
|
||||
err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point));
|
||||
dev_dbg(dev, "Program init returned %d\n", err);
|
||||
if (err != 0)
|
||||
goto abort;
|
||||
@ -176,7 +176,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
|
||||
goto abort;
|
||||
|
||||
/* Tell card we've finished */
|
||||
err = hermesi_program_end(hw);
|
||||
err = hw->ops->program_end(hw);
|
||||
dev_dbg(dev, "Program end returned %d\n", err);
|
||||
if (err != 0)
|
||||
goto abort;
|
||||
@ -223,7 +223,7 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
|
||||
if (!pda)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1);
|
||||
ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
|
||||
if (ret)
|
||||
goto free;
|
||||
}
|
||||
|
@ -51,6 +51,26 @@
|
||||
#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */
|
||||
#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
|
||||
|
||||
/*
|
||||
* 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_DISABLED 0x0000 /* Auxiliary port is closed */
|
||||
|
||||
#define HERMES_AUX_PW0 0xFE01
|
||||
#define HERMES_AUX_PW1 0xDC23
|
||||
#define HERMES_AUX_PW2 0xBA45
|
||||
|
||||
/* HERMES_CMD_DOWNLD */
|
||||
#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD)
|
||||
|
||||
/*
|
||||
* Debugging helpers
|
||||
*/
|
||||
@ -170,6 +190,7 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
|
||||
hw->iobase = address;
|
||||
hw->reg_spacing = reg_spacing;
|
||||
hw->inten = 0x0;
|
||||
hw->eeprom_pda = false;
|
||||
hw->ops = &hermes_ops_local;
|
||||
}
|
||||
EXPORT_SYMBOL(hermes_struct_init);
|
||||
@ -529,6 +550,189 @@ static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*** Hermes AUX control ***/
|
||||
|
||||
static inline void
|
||||
hermes_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));
|
||||
}
|
||||
|
||||
static inline int
|
||||
hermes_aux_control(hermes_t *hw, int enabled)
|
||||
{
|
||||
int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
|
||||
int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
|
||||
int i;
|
||||
|
||||
/* Already open? */
|
||||
if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
|
||||
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, action);
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
udelay(10);
|
||||
if (hermes_read_reg(hw, HERMES_CONTROL) ==
|
||||
desired_state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*** Hermes programming ***/
|
||||
|
||||
/* About to start programming data (Hermes I)
|
||||
* offset is the entry point
|
||||
*
|
||||
* Spectrum_cs' Symbol fw does not require this
|
||||
* wl_lkm Agere fw does
|
||||
* Don't know about intersil
|
||||
*/
|
||||
static int hermesi_program_init(hermes_t *hw, u32 offset)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Disable interrupts?*/
|
||||
/*hw->inten = 0x0;*/
|
||||
/*hermes_write_regn(hw, INTEN, 0);*/
|
||||
/*hermes_set_irqmask(hw, 0);*/
|
||||
|
||||
/* Acknowledge any outstanding command */
|
||||
hermes_write_regn(hw, EVACK, 0xFFFF);
|
||||
|
||||
/* Using init_cmd_wait rather than cmd_wait */
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
0x0100 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
0x0000 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hermes_aux_control(hw, 1);
|
||||
pr_debug("AUX enable returned %d\n", err);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("Enabling volatile, EP 0x%08x\n", offset);
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
HERMES_PROGRAM_ENABLE_VOLATILE,
|
||||
offset & 0xFFFFu,
|
||||
offset >> 16,
|
||||
0,
|
||||
NULL);
|
||||
pr_debug("PROGRAM_ENABLE returned %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Done programming data (Hermes I)
|
||||
*
|
||||
* Spectrum_cs' Symbol fw does not require this
|
||||
* wl_lkm Agere fw does
|
||||
* Don't know about intersil
|
||||
*/
|
||||
static int hermesi_program_end(hermes_t *hw)
|
||||
{
|
||||
struct hermes_response resp;
|
||||
int rc = 0;
|
||||
int err;
|
||||
|
||||
rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
|
||||
|
||||
pr_debug("PROGRAM_DISABLE returned %d, "
|
||||
"r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
|
||||
rc, resp.resp0, resp.resp1, resp.resp2);
|
||||
|
||||
if ((rc == 0) &&
|
||||
((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
|
||||
rc = -EIO;
|
||||
|
||||
err = hermes_aux_control(hw, 0);
|
||||
pr_debug("AUX disable returned %d\n", err);
|
||||
|
||||
/* Acknowledge any outstanding command */
|
||||
hermes_write_regn(hw, EVACK, 0xFFFF);
|
||||
|
||||
/* Reinitialise, ignoring return */
|
||||
(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
|
||||
return rc ? rc : err;
|
||||
}
|
||||
|
||||
static int hermes_program_bytes(struct hermes *hw, const char *data,
|
||||
u32 addr, u32 len)
|
||||
{
|
||||
/* wl lkm splits the programming into chunks of 2000 bytes.
|
||||
* This restriction appears to come from USB. The PCMCIA
|
||||
* adapters can program the whole lot in one go */
|
||||
hermes_aux_setaddr(hw, addr);
|
||||
hermes_write_bytes(hw, HERMES_AUXDATA, data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read PDA from the adapter */
|
||||
static int hermes_read_pda(hermes_t *hw, __le16 *pda, u32 pda_addr, u16 pda_len)
|
||||
{
|
||||
int ret;
|
||||
u16 pda_size;
|
||||
u16 data_len = pda_len;
|
||||
__le16 *data = pda;
|
||||
|
||||
if (hw->eeprom_pda) {
|
||||
/* PDA of spectrum symbol is in eeprom */
|
||||
|
||||
/* Issue command to read EEPROM */
|
||||
ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* wl_lkm does not include PDA size in the PDA area.
|
||||
* We will pad the information into pda, so other routines
|
||||
* don't have to be modified */
|
||||
pda[0] = cpu_to_le16(pda_len - 2);
|
||||
/* Includes CFG_PROD_DATA but not itself */
|
||||
pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
|
||||
data_len = pda_len - 4;
|
||||
data = pda + 2;
|
||||
}
|
||||
|
||||
/* Open auxiliary port */
|
||||
ret = hermes_aux_control(hw, 1);
|
||||
pr_debug("AUX enable returned %d\n", ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read PDA */
|
||||
hermes_aux_setaddr(hw, pda_addr);
|
||||
hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
|
||||
|
||||
/* Close aux port */
|
||||
ret = hermes_aux_control(hw, 0);
|
||||
pr_debug("AUX disable returned %d\n", ret);
|
||||
|
||||
/* Check PDA length */
|
||||
pda_size = le16_to_cpu(pda[0]);
|
||||
pr_debug("Actual PDA length %d, Max allowed %d\n",
|
||||
pda_size, pda_len);
|
||||
if (pda_size > pda_len)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hermes_lock_irqsave(spinlock_t *lock,
|
||||
unsigned long *flags) __acquires(lock)
|
||||
{
|
||||
@ -561,6 +765,10 @@ static const struct hermes_ops hermes_ops_local = {
|
||||
.write_ltv = hermes_write_ltv,
|
||||
.bap_pread = hermes_bap_pread,
|
||||
.bap_pwrite = hermes_bap_pwrite,
|
||||
.read_pda = hermes_read_pda,
|
||||
.program_init = hermesi_program_init,
|
||||
.program_end = hermesi_program_end,
|
||||
.program = hermes_program_bytes,
|
||||
.lock_irqsave = hermes_lock_irqsave,
|
||||
.unlock_irqrestore = hermes_unlock_irqrestore,
|
||||
.lock_irq = hermes_lock_irq,
|
||||
|
@ -393,6 +393,12 @@ struct hermes_ops {
|
||||
u16 id, u16 offset);
|
||||
int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
|
||||
int len, u16 id, u16 offset);
|
||||
int (*read_pda)(struct hermes *hw, __le16 *pda,
|
||||
u32 pda_addr, u16 pda_len);
|
||||
int (*program_init)(struct hermes *hw, u32 entry_point);
|
||||
int (*program_end)(struct hermes *hw);
|
||||
int (*program)(struct hermes *hw, const char *buf,
|
||||
u32 addr, u32 len);
|
||||
void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags);
|
||||
void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags);
|
||||
void (*lock_irq)(spinlock_t *lock);
|
||||
@ -406,6 +412,7 @@ typedef struct hermes {
|
||||
#define HERMES_16BIT_REGSPACING 0
|
||||
#define HERMES_32BIT_REGSPACING 1
|
||||
u16 inten; /* Which interrupts should be enabled? */
|
||||
bool eeprom_pda;
|
||||
const struct hermes_ops *ops;
|
||||
void *priv;
|
||||
} hermes_t;
|
||||
|
@ -46,37 +46,11 @@
|
||||
|
||||
#define PFX "hermes_dld: "
|
||||
|
||||
/*
|
||||
* 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_DISABLED 0x0000 /* Auxiliary port is closed */
|
||||
|
||||
#define HERMES_AUX_PW0 0xFE01
|
||||
#define HERMES_AUX_PW1 0xDC23
|
||||
#define HERMES_AUX_PW2 0xBA45
|
||||
|
||||
/* HERMES_CMD_DOWNLD */
|
||||
#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD)
|
||||
|
||||
/* End markers used in dblocks */
|
||||
#define PDI_END 0x00000000 /* End of PDA */
|
||||
#define BLOCK_END 0xFFFFFFFF /* Last image block */
|
||||
#define TEXT_END 0x1A /* End of text header */
|
||||
|
||||
/* Limit the amout we try to download in a single shot.
|
||||
* Size is in bytes.
|
||||
*/
|
||||
#define MAX_DL_SIZE 1024
|
||||
#define LIMIT_PROGRAM_SIZE 0
|
||||
|
||||
/*
|
||||
* The following structures have little-endian fields denoted by
|
||||
* the leading underscore. Don't access them directly - use inline
|
||||
@ -165,41 +139,6 @@ pdi_len(const struct pdi *pdi)
|
||||
return 2 * (le16_to_cpu(pdi->len) - 1);
|
||||
}
|
||||
|
||||
/*** Hermes AUX control ***/
|
||||
|
||||
static inline void
|
||||
hermes_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));
|
||||
}
|
||||
|
||||
static inline int
|
||||
hermes_aux_control(hermes_t *hw, int enabled)
|
||||
{
|
||||
int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
|
||||
int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
|
||||
int i;
|
||||
|
||||
/* Already open? */
|
||||
if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
|
||||
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, action);
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
udelay(10);
|
||||
if (hermes_read_reg(hw, HERMES_CONTROL) ==
|
||||
desired_state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*** Plug Data Functions ***/
|
||||
|
||||
/*
|
||||
@ -271,62 +210,7 @@ hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr,
|
||||
return -EINVAL;
|
||||
|
||||
/* do the actual plugging */
|
||||
hermes_aux_setaddr(hw, pdr_addr(pdr));
|
||||
hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read PDA from the adapter */
|
||||
int hermes_read_pda(hermes_t *hw,
|
||||
__le16 *pda,
|
||||
u32 pda_addr,
|
||||
u16 pda_len,
|
||||
int use_eeprom) /* can we get this into hw? */
|
||||
{
|
||||
int ret;
|
||||
u16 pda_size;
|
||||
u16 data_len = pda_len;
|
||||
__le16 *data = pda;
|
||||
|
||||
if (use_eeprom) {
|
||||
/* PDA of spectrum symbol is in eeprom */
|
||||
|
||||
/* Issue command to read EEPROM */
|
||||
ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* wl_lkm does not include PDA size in the PDA area.
|
||||
* We will pad the information into pda, so other routines
|
||||
* don't have to be modified */
|
||||
pda[0] = cpu_to_le16(pda_len - 2);
|
||||
/* Includes CFG_PROD_DATA but not itself */
|
||||
pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
|
||||
data_len = pda_len - 4;
|
||||
data = pda + 2;
|
||||
}
|
||||
|
||||
/* Open auxiliary port */
|
||||
ret = hermes_aux_control(hw, 1);
|
||||
pr_debug(PFX "AUX enable returned %d\n", ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* read PDA from EEPROM */
|
||||
hermes_aux_setaddr(hw, pda_addr);
|
||||
hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
|
||||
|
||||
/* Close aux port */
|
||||
ret = hermes_aux_control(hw, 0);
|
||||
pr_debug(PFX "AUX disable returned %d\n", ret);
|
||||
|
||||
/* Check PDA length */
|
||||
pda_size = le16_to_cpu(pda[0]);
|
||||
pr_debug(PFX "Actual PDA length %d, Max allowed %d\n",
|
||||
pda_size, pda_len);
|
||||
if (pda_size > pda_len)
|
||||
return -EINVAL;
|
||||
hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -389,101 +273,13 @@ hermes_blocks_length(const char *first_block, const void *end)
|
||||
|
||||
/*** Hermes programming ***/
|
||||
|
||||
/* About to start programming data (Hermes I)
|
||||
* offset is the entry point
|
||||
*
|
||||
* Spectrum_cs' Symbol fw does not require this
|
||||
* wl_lkm Agere fw does
|
||||
* Don't know about intersil
|
||||
*/
|
||||
int hermesi_program_init(hermes_t *hw, u32 offset)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Disable interrupts?*/
|
||||
/*hw->inten = 0x0;*/
|
||||
/*hermes_write_regn(hw, INTEN, 0);*/
|
||||
/*hermes_set_irqmask(hw, 0);*/
|
||||
|
||||
/* Acknowledge any outstanding command */
|
||||
hermes_write_regn(hw, EVACK, 0xFFFF);
|
||||
|
||||
/* Using init_cmd_wait rather than cmd_wait */
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
0x0100 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
0x0000 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hermes_aux_control(hw, 1);
|
||||
pr_debug(PFX "AUX enable returned %d\n", err);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset);
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
HERMES_PROGRAM_ENABLE_VOLATILE,
|
||||
offset & 0xFFFFu,
|
||||
offset >> 16,
|
||||
0,
|
||||
NULL);
|
||||
pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Done programming data (Hermes I)
|
||||
*
|
||||
* Spectrum_cs' Symbol fw does not require this
|
||||
* wl_lkm Agere fw does
|
||||
* Don't know about intersil
|
||||
*/
|
||||
int hermesi_program_end(hermes_t *hw)
|
||||
{
|
||||
struct hermes_response resp;
|
||||
int rc = 0;
|
||||
int err;
|
||||
|
||||
rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
|
||||
|
||||
pr_debug(PFX "PROGRAM_DISABLE returned %d, "
|
||||
"r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
|
||||
rc, resp.resp0, resp.resp1, resp.resp2);
|
||||
|
||||
if ((rc == 0) &&
|
||||
((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
|
||||
rc = -EIO;
|
||||
|
||||
err = hermes_aux_control(hw, 0);
|
||||
pr_debug(PFX "AUX disable returned %d\n", err);
|
||||
|
||||
/* Acknowledge any outstanding command */
|
||||
hermes_write_regn(hw, EVACK, 0xFFFF);
|
||||
|
||||
/* Reinitialise, ignoring return */
|
||||
(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
|
||||
return rc ? rc : err;
|
||||
}
|
||||
|
||||
/* Program the data blocks */
|
||||
int hermes_program(hermes_t *hw, const char *first_block, const void *end)
|
||||
{
|
||||
const struct dblock *blk;
|
||||
u32 blkaddr;
|
||||
u32 blklen;
|
||||
#if LIMIT_PROGRAM_SIZE
|
||||
u32 addr;
|
||||
u32 len;
|
||||
#endif
|
||||
int err = 0;
|
||||
|
||||
blk = (const struct dblock *) first_block;
|
||||
|
||||
@ -498,30 +294,10 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end)
|
||||
pr_debug(PFX "Programming block of length %d "
|
||||
"to address 0x%08x\n", blklen, blkaddr);
|
||||
|
||||
#if !LIMIT_PROGRAM_SIZE
|
||||
/* wl_lkm driver splits this into writes of 2000 bytes */
|
||||
hermes_aux_setaddr(hw, blkaddr);
|
||||
hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
|
||||
blklen);
|
||||
#else
|
||||
len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
|
||||
addr = blkaddr;
|
||||
err = hw->ops->program(hw, blk->data, blkaddr, blklen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
while (addr < (blkaddr + blklen)) {
|
||||
pr_debug(PFX "Programming subblock of length %d "
|
||||
"to address 0x%08x. Data @ %p\n",
|
||||
len, addr, &blk->data[addr - blkaddr]);
|
||||
|
||||
hermes_aux_setaddr(hw, addr);
|
||||
hermes_write_bytes(hw, HERMES_AUXDATA,
|
||||
&blk->data[addr - blkaddr],
|
||||
len);
|
||||
|
||||
addr += len;
|
||||
len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
|
||||
(blkaddr + blklen - addr) : MAX_DL_SIZE;
|
||||
}
|
||||
#endif
|
||||
blk = (const struct dblock *) &blk->data[blklen];
|
||||
|
||||
if ((void *) blk > (end - sizeof(*blk)))
|
||||
@ -530,7 +306,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end)
|
||||
blkaddr = dblock_addr(blk);
|
||||
blklen = dblock_len(blk);
|
||||
}
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*** Default plugging data for Hermes I ***/
|
||||
@ -690,9 +466,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
|
||||
if ((pdi_len(pdi) == pdr_len(pdr)) &&
|
||||
((void *) pdi->data + pdi_len(pdi) < pda_end)) {
|
||||
/* do the actual plugging */
|
||||
hermes_aux_setaddr(hw, pdr_addr(pdr));
|
||||
hermes_write_bytes(hw, HERMES_AUXDATA,
|
||||
pdi->data, pdi_len(pdi));
|
||||
hw->ops->program(hw, pdi->data, pdr_addr(pdr),
|
||||
pdi_len(pdi));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,11 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");
|
||||
#define EZUSB_RID_RX 0x0701
|
||||
#define EZUSB_RID_INIT1 0x0702
|
||||
#define EZUSB_RID_ACK 0x0710
|
||||
#define EZUSB_RID_READ_PDA 0x0800
|
||||
#define EZUSB_RID_PROG_INIT 0x0852
|
||||
#define EZUSB_RID_PROG_SET_ADDR 0x0853
|
||||
#define EZUSB_RID_PROG_BYTES 0x0854
|
||||
#define EZUSB_RID_PROG_END 0x0855
|
||||
#define EZUSB_RID_DOCMD 0x0860
|
||||
|
||||
/* Recognize info frames */
|
||||
@ -198,6 +203,8 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");
|
||||
|
||||
#define BULK_BUF_SIZE 2048
|
||||
|
||||
#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet))
|
||||
|
||||
#define FW_BUF_SIZE 64
|
||||
#define FW_VAR_OFFSET_PTR 0x359
|
||||
#define FW_VAR_VALUE 0
|
||||
@ -1077,6 +1084,115 @@ static int ezusb_bap_pread(struct hermes *hw, int bap,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ezusb_read_pda(struct hermes *hw, __le16 *pda,
|
||||
u32 pda_addr, u16 pda_len)
|
||||
{
|
||||
struct ezusb_priv *upriv = hw->priv;
|
||||
struct request_context *ctx;
|
||||
__le16 data[] = {
|
||||
cpu_to_le16(pda_addr & 0xffff),
|
||||
cpu_to_le16(pda_len - 4)
|
||||
};
|
||||
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
/* wl_lkm does not include PDA size in the PDA area.
|
||||
* We will pad the information into pda, so other routines
|
||||
* don't have to be modified */
|
||||
pda[0] = cpu_to_le16(pda_len - 2);
|
||||
/* Includes CFG_PROD_DATA but not itself */
|
||||
pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
|
||||
|
||||
return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
|
||||
EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int ezusb_program_init(struct hermes *hw, u32 entry_point)
|
||||
{
|
||||
struct ezusb_priv *upriv = hw->priv;
|
||||
struct request_context *ctx;
|
||||
__le32 data = cpu_to_le32(entry_point);
|
||||
|
||||
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
|
||||
EZUSB_FRAME_CONTROL, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static int ezusb_program_end(struct hermes *hw)
|
||||
{
|
||||
struct ezusb_priv *upriv = hw->priv;
|
||||
struct request_context *ctx;
|
||||
|
||||
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return ezusb_access_ltv(upriv, ctx, 0, NULL,
|
||||
EZUSB_FRAME_CONTROL, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static int ezusb_program_bytes(struct hermes *hw, const char *buf,
|
||||
u32 addr, u32 len)
|
||||
{
|
||||
struct ezusb_priv *upriv = hw->priv;
|
||||
struct request_context *ctx;
|
||||
__le32 data = cpu_to_le32(addr);
|
||||
int err;
|
||||
|
||||
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
|
||||
EZUSB_FRAME_CONTROL, NULL, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return ezusb_access_ltv(upriv, ctx, len, buf,
|
||||
EZUSB_FRAME_CONTROL, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static int ezusb_program(struct hermes *hw, const char *buf,
|
||||
u32 addr, u32 len)
|
||||
{
|
||||
u32 ch_addr;
|
||||
u32 ch_len;
|
||||
int err = 0;
|
||||
|
||||
/* We can only send 2048 bytes out of the bulk xmit at a time,
|
||||
* so we have to split any programming into chunks of <2048
|
||||
* bytes. */
|
||||
|
||||
ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE;
|
||||
ch_addr = addr;
|
||||
|
||||
while (ch_addr < (addr + len)) {
|
||||
pr_debug("Programming subblock of length %d "
|
||||
"to address 0x%08x. Data @ %p\n",
|
||||
ch_len, ch_addr, &buf[ch_addr - addr]);
|
||||
|
||||
err = ezusb_program_bytes(hw, &buf[ch_addr - addr],
|
||||
ch_addr, ch_len);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
ch_addr += ch_len;
|
||||
ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ?
|
||||
(addr + len - ch_addr) : MAX_DL_SIZE;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct orinoco_private *priv = ndev_priv(dev);
|
||||
@ -1440,6 +1556,10 @@ static const struct hermes_ops ezusb_ops = {
|
||||
.read_ltv = ezusb_read_ltv,
|
||||
.write_ltv = ezusb_write_ltv,
|
||||
.bap_pread = ezusb_bap_pread,
|
||||
.read_pda = ezusb_read_pda,
|
||||
.program_init = ezusb_program_init,
|
||||
.program_end = ezusb_program_end,
|
||||
.program = ezusb_program,
|
||||
.lock_irqsave = ezusb_lock_irqsave,
|
||||
.unlock_irqrestore = ezusb_unlock_irqrestore,
|
||||
.lock_irq = ezusb_lock_irq,
|
||||
|
@ -349,6 +349,7 @@ spectrum_cs_config(struct pcmcia_device *link)
|
||||
goto failed;
|
||||
|
||||
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
|
||||
hw->eeprom_pda = true;
|
||||
|
||||
/*
|
||||
* This actually configures the PCMCIA socket -- setting up
|
||||
|
Loading…
Reference in New Issue
Block a user