/* The card ID and card revision, as reported by the hardware. */
u16 card_id;
u8 card_rev;
/* The last time that we initiated an SPU operation */ unsignedlong prev_xfer_time;
int use_dummy_writes; unsignedlong spu_port_delay; unsignedlong spu_reg_delay;
/* Handles all SPI communication (except for FW load) */ struct workqueue_struct *workqueue; struct work_struct packet_work; struct work_struct resume_work;
u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE];
/* A buffer of incoming packets from libertas core. * Since we can't sleep in hw_host_to_card, we have to buffer
* them. */ struct list_head cmd_packet_list; struct list_head data_packet_list;
/* Protects cmd_packet_list and data_packet_list */
spinlock_t buffer_lock;
/* * SPI Interface Unit Routines * * The SPU sits between the host and the WLAN module. * All communication with the firmware is through SPU transactions. * * First we have to put a SPU register name on the bus. Then we can * either read from or write to that register. *
*/
staticvoid spu_transaction_init(struct if_spi_card *card)
{ if (!time_after(jiffies, card->prev_xfer_time + 1)) { /* Unfortunately, the SPU requires a delay between successive * transactions. If our last transaction was more than a jiffy * ago, we have obviously already delayed enough.
* If not, we have to busy-wait to be on the safe side. */
ndelay(400);
}
}
/* * Write out a byte buffer to an SPI register, * using a series of 16-bit transfers.
*/ staticint spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
{ int err = 0;
__le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); struct spi_message m; struct spi_transfer reg_trans; struct spi_transfer data_trans;
/* * Keep reading 16 bits from an SPI register until you get the correct result. * * If mask = 0, the correct result is any non-zero number. * If mask != 0, the correct result is any number where * number & target_mask == target * * Returns -ETIMEDOUT if a second passes without the correct result.
*/ staticint spu_wait_for_u16(struct if_spi_card *card, u16 reg,
u16 target_mask, u16 target)
{ int err; unsignedlong timeout = jiffies + 5*HZ; while (1) {
u16 val;
err = spu_read_u16(card, reg, &val); if (err) return err; if (target_mask) { if ((val & target_mask) == target) return 0;
} else { if (val) return 0;
}
udelay(100); if (time_after(jiffies, timeout)) {
pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n",
__func__, val, target_mask, target); return -ETIMEDOUT;
}
}
}
/* * Read 16 bits from an SPI register until you receive a specific value. * Returns -ETIMEDOUT if a 4 tries pass without success.
*/ staticint spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)
{ int err, try; for (try = 0; try < 4; ++try) {
u32 val = 0;
err = spu_read_u32(card, reg, &val); if (err) return err; if (val == target) return 0;
mdelay(100);
} return -ETIMEDOUT;
}
staticint spu_set_interrupt_mode(struct if_spi_card *card, int suppress_host_int, int auto_int)
{ int err = 0;
/* * We can suppress a host interrupt by clearing the appropriate * bit in the "host interrupt status mask" register
*/ if (suppress_host_int) {
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); if (err) return err;
} else {
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
IF_SPI_HISM_TX_DOWNLOAD_RDY |
IF_SPI_HISM_RX_UPLOAD_RDY |
IF_SPI_HISM_CMD_DOWNLOAD_RDY |
IF_SPI_HISM_CARDEVENT |
IF_SPI_HISM_CMD_UPLOAD_RDY); if (err) return err;
}
/* * If auto-interrupts are on, the completion of certain transactions * will trigger an interrupt automatically. If auto-interrupts * are off, we need to set the "Card Interrupt Cause" register to * trigger a card interrupt.
*/ if (auto_int) {
err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); if (err) return err;
} else {
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); if (err) return err;
} return err;
}
staticint spu_set_bus_mode(struct if_spi_card *card, u16 mode)
{ int err = 0;
u16 rval; /* set bus mode */
err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); if (err) return err; /* Check that we were able to read back what we just wrote. */
err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); if (err) return err; if ((rval & 0xF) != mode) {
pr_err("Can't read bus mode register\n"); return -EIO;
} return 0;
}
staticint spu_init(struct if_spi_card *card, int use_dummy_writes)
{ int err = 0;
u32 delay;
/* * We have to start up in timed delay mode so that we can safely * read the Delay Read Register.
*/
card->use_dummy_writes = 0;
err = spu_set_bus_mode(card,
IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); if (err) return err;
card->spu_port_delay = 1000;
card->spu_reg_delay = 1000;
err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); if (err) return err;
card->spu_port_delay = delay & 0x0000ffff;
card->spu_reg_delay = (delay & 0xffff0000) >> 16;
/* If dummy clock delay mode has been requested, switch to it now */ if (use_dummy_writes) {
card->use_dummy_writes = 1;
err = spu_set_bus_mode(card,
IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK |
IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); if (err) return err;
}
/* Load helper firmware image */ while (bytes_remaining > 0) { /* * Scratch pad 1 should contain the number of bytes we * want to download to the firmware
*/
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
HELPER_FW_LOAD_CHUNK_SZ); if (err) goto out;
err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
IF_SPI_HIST_CMD_DOWNLOAD_RDY,
IF_SPI_HIST_CMD_DOWNLOAD_RDY); if (err) goto out;
/* * Feed the data into the command read/write port reg * in chunks of 64 bytes
*/
memset(temp, 0, sizeof(temp));
memcpy(temp, fw,
min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ));
mdelay(10);
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
temp, HELPER_FW_LOAD_CHUNK_SZ); if (err) goto out;
/* * Once the helper / single stage firmware download is complete, * write 0 to scratch pad 1 and interrupt the * bootloader. This completes the helper download.
*/
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); if (err) goto out;
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); if (err) goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
out: if (err)
pr_err("failed to load helper firmware (err=%d)\n", err);
return err;
}
/* * Returns the length of the next packet the firmware expects us to send. * Sets crc_err if the previous transfer had a CRC error.
*/ staticint if_spi_prog_main_firmware_check_len(struct if_spi_card *card, int *crc_err)
{
u16 len; int err = 0;
/* * wait until the host interrupt status register indicates * that we are ready to download
*/
err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
IF_SPI_HIST_CMD_DOWNLOAD_RDY,
IF_SPI_HIST_CMD_DOWNLOAD_RDY); if (err) {
pr_err("timed out waiting for host_int_status\n"); return err;
}
/* Ask the device how many bytes of firmware it wants. */
err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); if (err) return err;
if (len > IF_SPI_CMD_BUF_SIZE) {
pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n",
len); return -EIO;
} if (len & 0x1) {
lbs_deb_spi("%s: crc error\n", __func__);
len &= ~0x1;
*crc_err = 1;
} else
*crc_err = 0;
err = spu_set_interrupt_mode(card, 1, 0); if (err) goto out;
err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); if (err) {
netdev_err(priv->dev, "%s: timed out waiting for initial scratch reg = 0\n",
__func__); goto out;
}
num_crc_errs = 0;
prev_len = 0;
bytes = firmware->size;
fw = firmware->data; while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { if (len < 0) {
err = len; goto out;
} if (bytes < 0) { /* * If there are no more bytes left, we would normally * expect to have terminated with len = 0
*/
netdev_err(priv->dev, "Firmware load wants more bytes than we have to offer.\n"); break;
} if (crc_err) { /* Previous transfer failed. */ if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) {
pr_err("Too many CRC errors encountered in firmware load.\n");
err = -EIO; goto out;
}
} else { /* Previous transfer succeeded. Advance counters. */
bytes -= prev_len;
fw += prev_len;
} if (bytes < len) {
memset(card->cmd_buffer, 0, len);
memcpy(card->cmd_buffer, fw, bytes);
} else
memcpy(card->cmd_buffer, fw, len);
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); if (err) goto out;
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
card->cmd_buffer, len); if (err) goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
IF_SPI_CIC_CMD_DOWNLOAD_OVER); if (err) goto out;
prev_len = len;
} if (bytes > prev_len) {
pr_err("firmware load wants fewer bytes than we have to offer\n");
}
/* Confirm firmware download */
err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
SUCCESSFUL_FW_DOWNLOAD_MAGIC); if (err) {
pr_err("failed to confirm the firmware download\n"); goto out;
}
out: if (err)
pr_err("failed to load firmware (err=%d)\n", err);
return err;
}
/* * SPI Transfer Thread * * The SPI worker handles all SPI transfers, so there is no need for a lock.
*/
/* Move a command from the card to the host */ staticint if_spi_c2h_cmd(struct if_spi_card *card)
{ struct lbs_private *priv = card->priv; unsignedlong flags; int err = 0;
u16 len;
u8 i;
/* * We need a buffer big enough to handle whatever people send to * hw_host_to_card
*/
BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE);
BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE);
/* * It's just annoying if the buffer size isn't a multiple of 4, because * then we might have len < IF_SPI_CMD_BUF_SIZE but * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE
*/
BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0);
/* How many bytes are there to read? */
err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); if (err) goto out; if (!len) {
netdev_err(priv->dev, "%s: error: card has no data for host\n",
__func__);
err = -EINVAL; goto out;
} elseif (len > IF_SPI_CMD_BUF_SIZE) {
netdev_err(priv->dev, "%s: error: response packet too large: %d bytes, but maximum is %d\n",
__func__, len, IF_SPI_CMD_BUF_SIZE);
err = -EINVAL; goto out;
}
/* Read the data from the WLAN module into our command buffer */
err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG,
card->cmd_buffer, ALIGN(len, 4)); if (err) goto out;
out: if (err)
netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
return err;
}
/* Move data from the card to the host */ staticint if_spi_c2h_data(struct if_spi_card *card)
{ struct lbs_private *priv = card->priv; struct sk_buff *skb; char *data;
u16 len; int err = 0;
/* How many bytes are there to read? */
err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); if (err) goto out; if (!len) {
netdev_err(priv->dev, "%s: error: card has no data for host\n",
__func__);
err = -EINVAL; goto out;
} elseif (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
netdev_err(priv->dev, "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n",
__func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
err = -EINVAL; goto out;
}
/* TODO: should we allocate a smaller skb if we have less data? */
skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); if (!skb) {
err = -ENOBUFS; goto out;
}
skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
data = skb_put(skb, len);
/* Read the data from the WLAN module into our skb... */
err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); if (err) {
dev_kfree_skb(skb); goto out;
}
/* pass the SKB to libertas */
err = lbs_process_rxed_packet(card->priv, skb); /* lbs_process_rxed_packet() consumes the skb */
out: if (err)
netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
return err;
}
/* Move data or a command from the host to the card. */ staticvoid if_spi_h2c(struct if_spi_card *card, struct if_spi_packet *packet, int type)
{ struct lbs_private *priv = card->priv; int err = 0;
u16 port_reg;
switch (type) { case MVMS_DAT:
port_reg = IF_SPI_DATA_RDWRPORT_REG; break; case MVMS_CMD:
port_reg = IF_SPI_CMD_RDWRPORT_REG; break; default:
netdev_err(priv->dev, "can't transfer buffer of type %d\n",
type);
err = -EINVAL; goto out;
}
/* Write the data to the card */
err = spu_write(card, port_reg, packet->buffer, packet->blen); if (err) goto out;
out:
kfree(packet);
if (err)
netdev_err(priv->dev, "%s: error %d\n", __func__, err);
}
/* Inform the host about a card event */ staticvoid if_spi_e2h(struct if_spi_card *card)
{ int err = 0;
u32 cause; struct lbs_private *priv = card->priv;
err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); if (err) goto out;
/* re-enable the card event interrupt */
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG,
~IF_SPI_HICU_CARD_EVENT); if (err) goto out;
/* generate a card interrupt */
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_HOST_EVENT); if (err) goto out;
lbs_queue_event(priv, cause & 0xff);
out: if (err)
netdev_err(priv->dev, "%s: error %d\n", __func__, err);
}
/* * Read the host interrupt status register to see what we * can do.
*/
err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
&hiStatus); if (err) {
netdev_err(priv->dev, "I/O error\n"); goto err;
}
if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) {
err = if_spi_c2h_cmd(card); if (err) goto err;
} if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) {
err = if_spi_c2h_data(card); if (err) goto err;
}
/* * workaround: in PS mode, the card does not set the Command * Download Ready bit, but it sets TX Download Ready.
*/ if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY ||
(card->priv->psstate != PS_STATE_FULL_POWER &&
(hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { /* * This means two things. First of all, * if there was a previous command sent, the card has * successfully received it. * Secondly, it is now ready to download another * command.
*/
lbs_host_to_card_done(card->priv);
/* Do we have any command packets from the host to send? */
packet = NULL;
spin_lock_irqsave(&card->buffer_lock, flags); if (!list_empty(&card->cmd_packet_list)) {
packet = (struct if_spi_packet *)(card->
cmd_packet_list.next);
list_del(&packet->list);
}
spin_unlock_irqrestore(&card->buffer_lock, flags);
if (packet)
if_spi_h2c(card, packet, MVMS_CMD);
} if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { /* Do we have any data packets from the host to send? */
packet = NULL;
spin_lock_irqsave(&card->buffer_lock, flags); if (!list_empty(&card->data_packet_list)) {
packet = (struct if_spi_packet *)(card->
data_packet_list.next);
list_del(&packet->list);
}
spin_unlock_irqrestore(&card->buffer_lock, flags);
if (packet)
if_spi_h2c(card, packet, MVMS_DAT);
} if (hiStatus & IF_SPI_HIST_CARD_EVENT)
if_spi_e2h(card);
/* * Host to Card * * Called from Libertas to transfer some data to the WLAN device * We can't sleep here.
*/ staticint if_spi_host_to_card(struct lbs_private *priv,
u8 type, u8 *buf, u16 nb)
{ int err = 0; unsignedlong flags; struct if_spi_card *card = priv->card; struct if_spi_packet *packet;
u16 blen;
/* * Host Interrupts * * Service incoming interrupts from the WLAN device. We can't sleep here, so * don't try to talk on the SPI bus, just queue the SPI xfer work.
*/ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
{ struct if_spi_card *card = dev_id;
err = spu_init(card, card->pdata->use_dummy_writes); if (err) goto out;
err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); if (err) goto out;
err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); if (err) goto out; if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
lbs_deb_spi("Firmware is already loaded for " "Marvell WLAN 802.11 adapter\n"); else { /* Check if we support this card */ for (i = 0; i < ARRAY_SIZE(fw_table); i++) { if (card->card_id == fw_table[i].model) break;
} if (i == ARRAY_SIZE(fw_table)) {
netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n",
card->card_id);
err = -ENODEV; goto out;
}
err = lbs_get_firmware(&card->spi->dev, card->card_id,
&fw_table[0], &helper, &mainfw); if (err) {
netdev_err(priv->dev, "failed to find firmware (%d)\n",
err); goto out;
}
err = request_irq(spi->irq, if_spi_host_interrupt,
IRQF_TRIGGER_FALLING, "libertas_spi", card); if (err) {
pr_err("can't get host irq line-- request_irq failed\n"); goto terminate_workqueue;
}
/* * Start the card. * This will call register_netdev, and we'll start * getting interrupts...
*/
err = lbs_start_card(priv); if (err) goto release_irq;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.