Skip to content

Commit

Permalink
orinoco_usb: implement fw download
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Kilroy authored and John W. Linville committed May 3, 2010
1 parent fc97431 commit 07cefe7
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 238 deletions.
8 changes: 4 additions & 4 deletions drivers/net/wireless/orinoco/fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
208 changes: 208 additions & 0 deletions drivers/net/wireless/orinoco/hermes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/wireless/orinoco/hermes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 07cefe7

Please sign in to comment.