[PATCH v2 3/3] libertas: if_spi, driver for libertas GSPI devices
Dan Williams
dcbw at redhat.com
Thu Jan 8 12:17:25 EST 2009
On Fri, 2009-01-02 at 19:00 -0800, Colin McCabe wrote:
> Add initial support for libertas devices using a GSPI interface. This has
> been tested with the 8686. At this time, as far as we know, GSPI firmware for
> this device is not publicly distributable, but we expect this to change.
>
> GSPI is intended to be used on embedded systems. Board-specific parameters are
> required (see libertas_spi.h).
One thing we found we *really* wanted with SDIO was the ability to reset
the card completely, is there any ability to do that with the SPI
interface, or do you need to rely on board-specific methods to do that?
More below...
> Signed-off-by: Colin McCabe <colin at cozybit.com>
> Signed-off-by: Andrey Yurovsky <andrey at cozybit.com>
> ---
> drivers/net/wireless/Kconfig | 6 +
> drivers/net/wireless/libertas/Makefile | 2 +
> drivers/net/wireless/libertas/defs.h | 2 +
> drivers/net/wireless/libertas/if_spi.c | 1216 ++++++++++++++++++++++++++++++++
> drivers/net/wireless/libertas/if_spi.h | 208 ++++++
> include/linux/spi/libertas_spi.h | 25 +
> 6 files changed, 1459 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/wireless/libertas/if_spi.c
> create mode 100644 drivers/net/wireless/libertas/if_spi.h
> create mode 100644 include/linux/spi/libertas_spi.h
>
> diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
> index ea543fc..6f9ee26 100644
> --- a/drivers/net/wireless/Kconfig
> +++ b/drivers/net/wireless/Kconfig
> @@ -151,6 +151,12 @@ config LIBERTAS_SDIO
> ---help---
> A driver for Marvell Libertas 8385 and 8686 SDIO devices.
>
> +config LIBERTAS_SPI
> + tristate "Marvell Libertas 8686 SPI 802.11b/g cards"
> + depends on LIBERTAS && SPI
> + ---help---
> + A driver for Marvell Libertas 8686 SPI devices.
> +
> config LIBERTAS_DEBUG
> bool "Enable full debugging output in the Libertas module."
> depends on LIBERTAS
> diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
> index 02080a3..0b69185 100644
> --- a/drivers/net/wireless/libertas/Makefile
> +++ b/drivers/net/wireless/libertas/Makefile
> @@ -4,8 +4,10 @@ libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o \
> usb8xxx-objs += if_usb.o
> libertas_cs-objs += if_cs.o
> libertas_sdio-objs += if_sdio.o
> +libertas_spi-objs += if_spi.o
>
> obj-$(CONFIG_LIBERTAS) += libertas.o
> obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
> obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o
> obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o
> +obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o
> diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
> index b7744d6..0d1494f 100644
> --- a/drivers/net/wireless/libertas/defs.h
> +++ b/drivers/net/wireless/libertas/defs.h
> @@ -41,6 +41,7 @@
> #define LBS_DEB_HEX 0x00200000
> #define LBS_DEB_SDIO 0x00400000
> #define LBS_DEB_SYSFS 0x00800000
> +#define LBS_DEB_SPI 0x01000000
>
> extern unsigned int lbs_debug;
>
> @@ -84,6 +85,7 @@ do { if ((lbs_debug & (grp)) == (grp)) \
> #define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args)
> #define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args)
> #define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args)
> +#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args)
>
> #define lbs_pr_info(format, args...) \
> printk(KERN_INFO DRV_NAME": " format, ## args)
> diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
> new file mode 100644
> index 0000000..6092574
> --- /dev/null
> +++ b/drivers/net/wireless/libertas/if_spi.c
> @@ -0,0 +1,1216 @@
> +/*
> + * linux/drivers/net/wireless/libertas/if_spi.c
> + *
> + * Driver for Marvell SPI WLAN cards.
> + *
> + * Copyright 2008 Analog Devices Inc.
> + *
> + * Authors:
> + * Andrey Yurovsky <andrey at cozybit.com>
> + * Colin McCabe <colin at cozybit.com>
> + *
> + * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/moduleparam.h>
> +#include <linux/firmware.h>
> +#include <linux/gpio.h>
> +#include <linux/jiffies.h>
> +#include <linux/kthread.h>
> +#include <linux/list.h>
> +#include <linux/netdevice.h>
> +#include <linux/spi/libertas_spi.h>
> +#include <linux/spi/spi.h>
> +
> +#include "host.h"
> +#include "decl.h"
> +#include "defs.h"
> +#include "dev.h"
> +#include "if_spi.h"
> +
> +struct if_spi_packet {
> + struct list_head list;
> + u16 blen;
> + u8 buffer[0] __attribute__((aligned(4)));
> +};
> +
> +struct if_spi_card {
> + struct spi_device *spi;
> + struct lbs_private *priv;
> +
> + char helper_fw_name[FIRMWARE_NAME_MAX];
> + char main_fw_name[FIRMWARE_NAME_MAX];
> +
> + /* The card ID and card revision, as reported by the hardware. */
> + u16 card_id;
> + u8 card_rev;
> +
> + /* Pin number for our GPIO chip-select. */
> + /* TODO: Once the generic SPI layer has some additional features, we
> + * should take this out and use the normal chip select here.
> + * We need support for chip select delays, and not dropping chipselect
> + * after each word. */
> + int gpio_cs;
> +
> + /* The last time that we initiated an SPU operation */
> + unsigned long prev_xfer_time;
> +
> + int use_dummy_writes;
> + unsigned long spu_port_delay;
> + unsigned long spu_reg_delay;
> +
> + /* Handles all SPI communication (except for FW load) */
> + struct task_struct *spi_thread;
> + int run_thread;
> +
> + /* Used to wake up the spi_thread */
> + struct semaphore spi_ready;
> + struct semaphore spi_thread_terminated;
Are these really using semaphore semantics, or would a mutex be more
appropriate here? A ton of stuff got converted to mutexes a while back,
so if you don't actually need the specific semaphore behavior mutexes
might be a clearer choice.
> + 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;
> +};
> +
> +static void free_if_spi_card(struct if_spi_card *card)
> +{
> + struct list_head *cursor, *next;
> + struct if_spi_packet *packet;
> +
> + BUG_ON(card->run_thread);
> + list_for_each_safe(cursor, next, &card->cmd_packet_list) {
> + packet = container_of(cursor, struct if_spi_packet, list);
> + list_del(&packet->list);
> + kfree(packet);
> + }
> + list_for_each_safe(cursor, next, &card->data_packet_list) {
> + packet = container_of(cursor, struct if_spi_packet, list);
> + list_del(&packet->list);
> + kfree(packet);
> + }
> + spi_set_drvdata(card->spi, NULL);
> + kfree(card);
> +}
> +
> +static struct chip_ident chip_id_to_device_name[] = {
> + { .chip_id = 0x04, .name = 8385 },
> + { .chip_id = 0x0b, .name = 8686 },
> +};
> +
> +/*
> + * 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.
> + *
> + * For 16-bit transactions, byte order on the bus is big-endian.
> + * We don't have to worry about that here, though.
> + * The translation takes place in the SPI routines.
> + */
> +
> +static void 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);
> + }
> + gpio_set_value(card->gpio_cs, 0); /* assert CS */
> +}
> +
> +static void spu_transaction_finish(struct if_spi_card *card)
> +{
> + gpio_set_value(card->gpio_cs, 1); /* drop CS */
> + card->prev_xfer_time = jiffies;
> +}
> +
> +/* Write out a byte buffer to an SPI register,
> + * using a series of 16-bit transfers. */
> +static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
> +{
> + int n;
> + int err = 0;
> + u16 reg_out = reg | IF_SPI_WRITE_OPERATION_MASK;
> +
> + /* You must give an even number of bytes to the SPU, even if it
> + * doesn't care about the last one. */
> + BUG_ON(len & 0x1);
> +
> + spu_transaction_init(card);
> +
> + /* write SPU register index */
> + err = spi_write(card->spi, (u8 *)®_out, sizeof(u16));
> + if (err)
> + goto out;
> +
> + for (n = 0; n < len; n += sizeof(u16)) {
> + err = spi_write(card->spi, buf + n, sizeof(u16));
> + if (err)
> + goto out;
> + }
> +
> +out:
> + spu_transaction_finish(card);
> + return err;
> +}
> +
> +static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val)
> +{
> + return spu_write(card, reg, (u8 *)&val, sizeof(u16));
> +}
> +
> +static inline int spu_write_u32(struct if_spi_card *card, u16 reg, u32 val)
> +{
> + /* The lower 16 bits are written first. */
> + u16 out[2];
> + out[0] = val & 0xffff;
> + out[1] = (val & 0xffff0000) >> 16;
> + return spu_write(card, reg, (u8 *)&out, sizeof(u32));
> +}
> +
> +static inline int spu_reg_is_port_reg(u16 reg)
> +{
> + switch (reg) {
> + case IF_SPI_IO_RDWRPORT_REG:
> + case IF_SPI_CMD_RDWRPORT_REG:
> + case IF_SPI_DATA_RDWRPORT_REG:
> + return 1;
> + default:
> + return 0;
> + }
> +}
> +
> +static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)
> +{
> + unsigned int i, delay;
> + int n, err = 0;
> + u16 zero = 0;
> + u16 reg_out = reg | IF_SPI_READ_OPERATION_MASK;
> +
> + /* You must take an even number of bytes from the SPU, even if you
> + * don't care about the last one. */
> + BUG_ON(len & 0x1);
> +
> + spu_transaction_init(card);
> +
> + /* write SPU register index */
> + err = spi_write(card->spi, (u8 *)®_out, sizeof(u16));
> + if (err)
Do you really want 'goto out' here instead of 'return' so that
spu_transaction_finish() gets called?
> + return err;
> +
> + delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay :
> + card->spu_reg_delay;
> + if (card->use_dummy_writes) {
> + /* Clock in dummy cycles while the SPU fills the FIFO */
> + for (i = 0; i < delay / 16; ++i) {
> + err = spi_write(card->spi, (u8 *)&zero, sizeof(u16));
> + if (err)
> + return err;
Same here.
> + }
> + } else {
> + /* Busy-wait while the SPU fills the FIFO */
> + ndelay(100 + (delay * 10));
> + }
> +
> + /* read in data */
> + for (n = 0; n < len; n += sizeof(u16)) {
> + err = spi_read(card->spi, buf + n, sizeof(u16));
> + if (err)
> + goto out;
> + }
> +
> +out:
> + spu_transaction_finish(card);
> + return err;
> +}
> +
> +/* Read 16 bits from an SPI register */
> +static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val)
> +{
> + return spu_read(card, reg, (u8 *)val, sizeof(u16));
> +}
> +
> +/* Read 32 bits from an SPI register.
> + * The low 16 bits are read first. */
> +static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)
> +{
> + u16 buf[2];
> + int err;
> + err = spu_read(card, reg, (u8 *)buf, sizeof(u32));
> + if (!err)
> + *val = buf[0] | (buf[1] << 16);
> + return err;
> +}
> +
> +/* 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. */
> +static int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
> + u16 target_mask, u16 target)
> +{
> + int err;
> + unsigned long 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)) {
> + lbs_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. */
> +static int 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;
> +}
> +
> +static int 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;
> +}
> +
> +static int spu_get_chip_revision(struct if_spi_card *card,
> + u16 *card_id, u8 *card_rev)
> +{
> + int err = 0;
> + u32 dev_ctrl;
> + err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
> + if (err)
> + return err;
> + *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
> + *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
> + return err;
> +}
> +
> +static int 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 != mode) {
> + lbs_pr_err("Can't read bus mode register.\n");
> + return -EIO;
> + }
> + return 0;
> +}
> +
> +static int 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;
> + }
> +
> + lbs_deb_spi("Initialized SPU unit. "
> + "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n",
> + card->spu_port_delay, card->spu_reg_delay);
> + return err;
> +}
> +
> +/*
> + * Firmware Loading
> + */
> +
> +static int if_spi_prog_helper_firmware(struct if_spi_card *card)
> +{
> + int err = 0;
> + const struct firmware *firmware = NULL;
> + int bytes_remaining;
> + const u8 *fw;
> + u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
> + struct spi_device *spi = card->spi;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + err = spu_set_interrupt_mode(card, 1, 0);
> + if (err)
> + goto out;
> + /* Get helper firmware image */
> + err = request_firmware(&firmware, card->helper_fw_name, &spi->dev);
> + if (err) {
> + lbs_pr_err("request_firmware failed with err = %d\n", err);
> + goto out;
> + }
> + bytes_remaining = firmware->size;
> + fw = firmware->data;
> +
> + /* 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 release_firmware;
> +
> + 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 release_firmware;
> +
> + /* 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 release_firmware;
> +
> + /* Interrupt the boot code */
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
> + IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> + if (err)
> + goto release_firmware;
> + bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
> + fw += HELPER_FW_LOAD_CHUNK_SZ;
> + }
> +
> + /* 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 release_firmware;
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
> + IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> + goto release_firmware;
> +
> + lbs_deb_spi("waiting for helper to boot...\n");
> +
> +release_firmware:
> + release_firmware(firmware);
> +out:
> + if (err)
> + lbs_pr_err("failed to load helper firmware (err=%d)\n", err);
> + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", 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. */
> +static int 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) {
> + lbs_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) {
> + lbs_pr_err("firmware load device requested a larger "
> + "tranfer 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;
> +
> + return len;
> +}
> +
> +static int if_spi_prog_main_firmware(struct if_spi_card *card)
> +{
> + int len, prev_len;
> + int bytes, crc_err = 0, err = 0;
> + const struct firmware *firmware = NULL;
> + const u8 *fw;
> + struct spi_device *spi = card->spi;
> + u16 num_crc_errs;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + err = spu_set_interrupt_mode(card, 1, 0);
> + if (err)
> + goto out;
> +
> + /* Get firmware image */
> + err = request_firmware(&firmware, card->main_fw_name, &spi->dev);
> + if (err) {
> + lbs_pr_err("%s: can't get firmware '%s' from kernel. "
> + "err = %d\n", __func__, card->main_fw_name, err);
> + goto out;
> + }
> +
> + err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
> + if (err) {
> + lbs_pr_err("%s: timed out waiting for initial "
> + "scratch reg = 0\n", __func__);
> + goto release_firmware;
> + }
> +
> + 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 release_firmware;
> + }
> + if (bytes < 0) {
> + /* If there are no more bytes left, we would normally
> + * expect to have terminated with len = 0 */
> + lbs_pr_err("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) {
> + lbs_pr_err("Too many CRC errors encountered "
> + "in firmware load.\n");
> + err = -EIO;
> + goto release_firmware;
> + }
> + } 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 release_firmware;
> + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
> + card->cmd_buffer, len);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
> + IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> + if (err)
> + goto release_firmware;
> + prev_len = len;
> + }
> + if (bytes > prev_len) {
> + lbs_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) {
> + lbs_pr_err("failed to confirm the firmware download\n");
> + goto release_firmware;
> + }
> +
> +release_firmware:
> + release_firmware(firmware);
> +
> +out:
> + if (err)
> + lbs_pr_err("failed to load firmware (err=%d)\n", err);
> + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
> + return err;
> +}
> +
> +/*
> + * SPI Transfer Thread
> + *
> + * The SPI thread handles all SPI transfers, so there is no need for a lock.
> + */
> +
> +/* Move a command from the card to the host */
> +static int if_spi_c2h_cmd(struct if_spi_card *card)
> +{
> + struct lbs_private *priv = card->priv;
> + unsigned long 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);
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + /* How many bytes are there to read? */
> + err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len);
> + if (err)
> + goto out;
> + if (!len) {
> + lbs_pr_err("%s: error: card has no data for host\n",
> + __func__);
> + err = -EINVAL;
> + goto out;
> + } else if (len > IF_SPI_CMD_BUF_SIZE) {
> + lbs_pr_err("%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;
> +
> + spin_lock_irqsave(&priv->driver_lock, flags);
> + i = (priv->resp_idx == 0) ? 1 : 0;
> + BUG_ON(priv->resp_len[i]);
> + priv->resp_len[i] = len;
> + memcpy(priv->resp_buf[i], card->cmd_buffer, len);
> + lbs_notify_command_response(priv, i);
> + spin_unlock_irqrestore(&priv->driver_lock, flags);
> +
> +out:
> + if (err)
> + lbs_pr_err("%s: err=%d\n", __func__, err);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return err;
> +}
> +
> +/* Move data from the card to the host */
> +static int if_spi_c2h_data(struct if_spi_card *card)
> +{
> + struct sk_buff *skb;
> + char *data;
> + u16 len;
> + int err = 0;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + /* How many bytes are there to read? */
> + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
> + if (err)
> + goto out;
> + if (!len) {
> + lbs_pr_err("%s: error: card has no data for host\n",
> + __func__);
> + err = -EINVAL;
> + goto out;
> + } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
> + lbs_pr_err("%s: error: card has %d bytes of data, but "
> + "our maximum skb size is %u\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)
> + goto free_skb;
> +
> + /* pass the SKB to libertas */
> + err = lbs_process_rxed_packet(card->priv, skb);
> + if (err)
> + goto free_skb;
> +
> + /* success */
> + goto out;
> +
> +free_skb:
> + dev_kfree_skb(skb);
> +out:
> + if (err)
> + lbs_pr_err("%s: err=%d\n", __func__, err);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return err;
> +}
> +
> +/* Move data or a command from the host to the card. */
> +static void if_spi_h2c(struct if_spi_card *card,
> + struct if_spi_packet *packet, int type)
> +{
> + int err = 0;
> + u16 int_type, port_reg;
> +
> + switch (type) {
> + case MVMS_DAT:
> + int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER;
> + port_reg = IF_SPI_DATA_RDWRPORT_REG;
> + break;
> + case MVMS_CMD:
> + int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER;
> + port_reg = IF_SPI_CMD_RDWRPORT_REG;
> + break;
> + default:
> + lbs_pr_err("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)
> + lbs_pr_err("%s: error %d\n", __func__, err);
> +}
> +
> +/* Inform the host about a card event */
> +static void if_spi_e2h(struct if_spi_card *card)
> +{
> + int err = 0;
> + unsigned long flags;
> + u32 cause;
> + struct lbs_private *priv = card->priv;
> +
> + err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause);
> + if (err)
> + goto out;
> +
> + spin_lock_irqsave(&priv->driver_lock, flags);
> + lbs_queue_event(priv, cause & 0xff);
> + spin_unlock_irqrestore(&priv->driver_lock, flags);
> +
> +out:
> + if (err)
> + lbs_pr_err("%s: error %d\n", __func__, err);
> +}
> +
> +static int lbs_spi_thread(void *data)
> +{
> + int err;
> + struct if_spi_card *card = data;
> + u16 hiStatus;
> + unsigned long flags;
> + struct if_spi_packet *packet;
> +
> + while (1) {
> + /* Wait to be woken up by one of two things. First, our ISR
> + * could tell us that something happened on the WLAN.
> + * Secondly, libertas could call hw_host_to_card with more
> + * data, which we might be able to send.
> + */
> + do {
> + err = down_interruptible(&card->spi_ready);
> + if (!card->run_thread) {
> + up(&card->spi_thread_terminated);
> + do_exit(0);
> + }
> + } while (err == EINTR);
> +
> + /* 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) {
> + lbs_pr_err("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;
> + if (hiStatus & IF_SPI_HIST_CMD_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);
> + }
> +
> + /* TODO: implement IF_SPI_HIST_CARD_EVENT
> + * Eventually this should feed into lbs_queue_event */
This comment can go as it looks like you've handled it already :)
> +err:
> + if (err)
> + lbs_pr_err("%s: got error %d\n", __func__, err);
> + }
> +}
> +
> +/* Block until lbs_spi_thread thread has terminated */
> +static void if_spi_terminate_spi_thread(struct if_spi_card *card)
> +{
> + /* It would be nice to use kthread_stop here, but that function
> + * can't wake threads waiting for a semaphore. */
> + card->run_thread = 0;
> + up(&card->spi_ready);
> + down(&card->spi_thread_terminated);
> +}
> +
> +/*
> + * Host to Card
> + *
> + * Called from Libertas to transfer some data to the WLAN device
> + * We can't sleep here. */
> +static int if_spi_host_to_card(struct lbs_private *priv,
> + u8 type, u8 *buf, u16 nb)
> +{
> + int err = 0;
> + unsigned long flags;
> + struct if_spi_card *card = priv->card;
> + struct if_spi_packet *packet;
> + u16 blen;
> +
> + lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb);
> +
> + if (nb == 0) {
> + lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb);
> + err = -EINVAL;
> + goto out;
> + }
> + blen = ALIGN(nb, 4);
> + packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC);
> + if (!packet) {
> + err = -ENOMEM;
> + goto out;
> + }
> + packet->blen = blen;
> + memcpy(packet->buffer, buf, nb);
> + memset(packet->buffer + nb, 0, blen - nb);
> +
> + switch (type) {
> + case MVMS_CMD:
> + priv->dnld_sent = DNLD_CMD_SENT;
> + spin_lock_irqsave(&card->buffer_lock, flags);
> + list_add_tail(&packet->list, &card->cmd_packet_list);
> + spin_unlock_irqrestore(&card->buffer_lock, flags);
> + break;
> + case MVMS_DAT:
> + priv->dnld_sent = DNLD_DATA_SENT;
> + spin_lock_irqsave(&card->buffer_lock, flags);
> + list_add_tail(&packet->list, &card->data_packet_list);
> + spin_unlock_irqrestore(&card->buffer_lock, flags);
> + break;
> + default:
> + lbs_pr_err("can't transfer buffer of type %d", type);
> + err = -EINVAL;
> + break;
> + }
> +
> + /* Wake up the spi thread */
> + up(&card->spi_ready);
> +out:
> + lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err);
> + return err;
> +}
> +
> +/*
> + * 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 wake up the SPI thread.
> + */
> +static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
> +{
> + struct if_spi_card *card = dev_id;
> +
> + up(&card->spi_ready);
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * SPI callbacks
> + */
> +
> +static int if_spi_calculate_fw_names(u16 card_id,
> + char *helper_fw, char *main_fw)
> +{
> + int i;
> + for (i = 0; i < ARRAY_SIZE(chip_id_to_device_name); ++i) {
> + if (card_id == chip_id_to_device_name[i].chip_id)
> + break;
> + }
> + if (i == ARRAY_SIZE(chip_id_to_device_name)) {
> + lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id);
> + return -EAFNOSUPPORT;
> + }
> + snprintf(helper_fw, FIRMWARE_NAME_MAX, "spi%d_helper.bin",
> + chip_id_to_device_name[i].name);
> + snprintf(main_fw, FIRMWARE_NAME_MAX, "spi%d.bin",
> + chip_id_to_device_name[i].name);
Could you look for firmware in the "libertas/" directory, and could we
make the prefix of the firmware be "gspi" to match the actual Marvell
firmware names?
The firmware (at least for gspi8686) is in the process of getting pushed
to the linux-firmware tree, and I'd like to consolidate libertas
firmware into the libertas/ subdirectory for all libertas interface
drivers.
Dan
> + return 0;
> +}
> +
> +static int __devinit if_spi_probe(struct spi_device *spi)
> +{
> + struct if_spi_card *card;
> + struct lbs_private *priv = NULL;
> + struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
> + int err = 0;
> + u32 scratch;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + /* Allocate card structure to represent this specific device */
> + card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
> + if (!card) {
> + err = -ENOMEM;
> + goto out;
> + }
> + spi_set_drvdata(spi, card);
> + card->spi = spi;
> + card->gpio_cs = pdata->gpio_cs;
> + card->prev_xfer_time = jiffies;
> +
> + sema_init(&card->spi_ready, 0);
> + sema_init(&card->spi_thread_terminated, 0);
> + INIT_LIST_HEAD(&card->cmd_packet_list);
> + INIT_LIST_HEAD(&card->data_packet_list);
> + spin_lock_init(&card->buffer_lock);
> +
> + /* set up GPIO CS line. TODO: use regular CS line */
> + err = gpio_request(card->gpio_cs, "if_spi_gpio_chip_select");
> + if (err)
> + goto free_card;
> + err = gpio_direction_output(card->gpio_cs, 1);
> + if (err)
> + goto free_gpio;
> +
> + /* Initialize the SPI Interface Unit */
> + err = spu_init(card, pdata->use_dummy_writes);
> + if (err)
> + goto free_gpio;
> + err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
> + if (err)
> + goto free_gpio;
> +
> + /* Firmware load */
> + err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
> + if (err)
> + goto free_gpio;
> + if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
> + lbs_deb_spi("Firmware is already loaded for "
> + "Marvell WLAN 802.11 adapter\n");
> + else {
> + err = if_spi_calculate_fw_names(card->card_id,
> + card->helper_fw_name, card->main_fw_name);
> + if (err)
> + goto free_gpio;
> +
> + lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
> + "(chip_id = 0x%04x, chip_rev = 0x%02x) "
> + "attached to SPI bus_num %d, chip_select %d. "
> + "spi->max_speed_hz=%d\n",
> + card->card_id, card->card_rev,
> + spi->master->bus_num, spi->chip_select,
> + spi->max_speed_hz);
> + err = if_spi_prog_helper_firmware(card);
> + if (err)
> + goto free_gpio;
> + err = if_spi_prog_main_firmware(card);
> + if (err)
> + goto free_gpio;
> + lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
> + }
> +
> + err = spu_set_interrupt_mode(card, 0, 1);
> + if (err)
> + goto free_gpio;
> +
> + /* Register our card with libertas.
> + * This will call alloc_etherdev */
> + priv = lbs_add_card(card, &spi->dev);
> + if (!priv) {
> + err = -ENOMEM;
> + goto free_gpio;
> + }
> + card->priv = priv;
> + priv->card = card;
> + priv->hw_host_to_card = if_spi_host_to_card;
> + priv->fw_ready = 1;
> + priv->ps_supported = 1;
> +
> + /* Initialize interrupt handling stuff. */
> + card->run_thread = 1;
> + card->spi_thread = kthread_run(lbs_spi_thread, card, "lbs_spi_thread");
> + if (IS_ERR(card->spi_thread)) {
> + card->run_thread = 0;
> + err = PTR_ERR(card->spi_thread);
> + lbs_pr_err("error creating SPI thread: err=%d\n", err);
> + goto remove_card;
> + }
> + err = request_irq(spi->irq, if_spi_host_interrupt,
> + IRQF_TRIGGER_FALLING, "libertas_spi", card);
> + if (err) {
> + lbs_pr_err("can't get host irq line-- request_irq failed\n");
> + goto terminate_thread;
> + }
> +
> + /* Start the card.
> + * This will call register_netdev, and we'll start
> + * getting interrupts... */
> + err = lbs_start_card(priv);
> + if (err)
> + goto release_irq;
> +
> + lbs_deb_spi("Finished initializing WLAN module.\n");
> +
> + /* successful exit */
> + goto out;
> +
> +release_irq:
> + free_irq(spi->irq, card);
> +terminate_thread:
> + if_spi_terminate_spi_thread(card);
> +remove_card:
> + lbs_remove_card(priv); /* will call free_netdev */
> +free_gpio:
> + gpio_free(card->gpio_cs);
> +free_card:
> + free_if_spi_card(card);
> +out:
> + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
> + return err;
> +}
> +
> +static int __devexit libertas_spi_remove(struct spi_device *spi)
> +{
> + struct if_spi_card *card = spi_get_drvdata(spi);
> + struct lbs_private *priv = card->priv;
> +
> + lbs_deb_spi("libertas_spi_remove\n");
> + lbs_deb_enter(LBS_DEB_SPI);
> + priv->surpriseremoved = 1;
> +
> + lbs_stop_card(priv);
> + free_irq(spi->irq, card);
> + if_spi_terminate_spi_thread(card);
> + lbs_remove_card(priv); /* will call free_netdev */
> + gpio_free(card->gpio_cs);
> + free_if_spi_card(card);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return 0;
> +}
> +
> +static struct spi_driver libertas_spi_driver = {
> + .probe = if_spi_probe,
> + .remove = __devexit_p(libertas_spi_remove),
> + .driver = {
> + .name = "libertas_spi",
> + .bus = &spi_bus_type,
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +/*
> + * Module functions
> + */
> +
> +static int __init if_spi_init_module(void)
> +{
> + int ret = 0;
> + lbs_deb_enter(LBS_DEB_SPI);
> + printk(KERN_INFO "libertas_spi: Libertas SPI driver\n");
> + ret = spi_register_driver(&libertas_spi_driver);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return ret;
> +}
> +
> +static void __exit if_spi_exit_module(void)
> +{
> + lbs_deb_enter(LBS_DEB_SPI);
> + spi_unregister_driver(&libertas_spi_driver);
> + lbs_deb_leave(LBS_DEB_SPI);
> +}
> +
> +module_init(if_spi_init_module);
> +module_exit(if_spi_exit_module);
> +
> +MODULE_DESCRIPTION("Libertas SPI WLAN Driver");
> +MODULE_AUTHOR("Andrey Yurovsky <andrey at cozybit.com>, "
> + "Colin McCabe <colin at cozybit.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h
> new file mode 100644
> index 0000000..2103869
> --- /dev/null
> +++ b/drivers/net/wireless/libertas/if_spi.h
> @@ -0,0 +1,208 @@
> +/*
> + * linux/drivers/net/wireless/libertas/if_spi.c
> + *
> + * Driver for Marvell SPI WLAN cards.
> + *
> + * Copyright 2008 Analog Devices Inc.
> + *
> + * Authors:
> + * Andrey Yurovsky <andrey at cozybit.com>
> + * Colin McCabe <colin at cozybit.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or (at
> + * your option) any later version.
> + */
> +
> +#ifndef _LBS_IF_SPI_H_
> +#define _LBS_IF_SPI_H_
> +
> +#define IPFIELD_ALIGN_OFFSET 2
> +#define IF_SPI_CMD_BUF_SIZE 2400
> +
> +/***************** Firmware *****************/
> +struct chip_ident {
> + u16 chip_id;
> + u16 name;
> +};
> +
> +#define MAX_MAIN_FW_LOAD_CRC_ERR 10
> +
> +/* Chunk size when loading the helper firmware */
> +#define HELPER_FW_LOAD_CHUNK_SZ 64
> +
> +/* Value to write to indicate end of helper firmware dnld */
> +#define FIRMWARE_DNLD_OK 0x0000
> +
> +/* Value to check once the main firmware is downloaded */
> +#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888
> +
> +/***************** SPI Interface Unit *****************/
> +/* Masks used in SPI register read/write operations */
> +#define IF_SPI_READ_OPERATION_MASK 0x0
> +#define IF_SPI_WRITE_OPERATION_MASK 0x8000
> +
> +/* SPI register offsets. 4-byte aligned. */
> +#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */
> +#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */
> +#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */
> +#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */
> +
> +#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */
> +#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */
> +#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */
> +
> +#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */
> +#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */
> +#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */
> +
> +#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */
> +#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */
> +#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */
> +#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */
> +
> +#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */
> +#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */
> +
> +#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */
> +
> +#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */
> +#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interupt status reg */
> +#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */
> +#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */
> +
> +#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */
> +
> +#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */
> +#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */
> +#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */
> +#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */
> +#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */
> +
> +#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */
> +#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */
> +
> +/***************** IF_SPI_DEVICEID_CTRL_REG *****************/
> +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16)
> +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff)
> +
> +/***************** IF_SPI_HOST_INT_CTRL_REG *****************/
> +/** Host Interrupt Control bit : Wake up */
> +#define IF_SPI_HICT_WAKE_UP (1<<0)
> +/** Host Interrupt Control bit : WLAN ready */
> +#define IF_SPI_HICT_WLAN_READY (1<<1)
> +/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */
> +/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */
> +/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */
> +/** Host Interrupt Control bit : Tx auto download */
> +#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5)
> +/** Host Interrupt Control bit : Rx auto upload */
> +#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6)
> +/** Host Interrupt Control bit : Command auto download */
> +#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7)
> +/** Host Interrupt Control bit : Command auto upload */
> +#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8)
> +
> +/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/
> +/** Card Interrupt Case bit : Tx download over */
> +#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0)
> +/** Card Interrupt Case bit : Rx upload over */
> +#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1)
> +/** Card Interrupt Case bit : Command download over */
> +#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2)
> +/** Card Interrupt Case bit : Host event */
> +#define IF_SPI_CIC_HOST_EVENT (1<<3)
> +/** Card Interrupt Case bit : Command upload over */
> +#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4)
> +/** Card Interrupt Case bit : Power down */
> +#define IF_SPI_CIC_POWER_DOWN (1<<5)
> +
> +/***************** IF_SPI_CARD_INT_STATUS_REG *****************/
> +#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0)
> +#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1)
> +#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2)
> +#define IF_SPI_CIS_HOST_EVENT (1<<3)
> +#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4)
> +#define IF_SPI_CIS_POWER_DOWN (1<<5)
> +
> +/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/
> +#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0)
> +#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1)
> +#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2)
> +#define IF_SPI_HICU_CARD_EVENT (1<<3)
> +#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4)
> +#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5)
> +#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6)
> +#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7)
> +#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8)
> +#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9)
> +#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10)
> +
> +/***************** IF_SPI_HOST_INT_STATUS_REG *****************/
> +/** Host Interrupt Status bit : Tx download ready */
> +#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0)
> +/** Host Interrupt Status bit : Rx upload ready */
> +#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1)
> +/** Host Interrupt Status bit : Command download ready */
> +#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2)
> +/** Host Interrupt Status bit : Card event */
> +#define IF_SPI_HIST_CARD_EVENT (1<<3)
> +/** Host Interrupt Status bit : Command upload ready */
> +#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4)
> +/** Host Interrupt Status bit : I/O write FIFO overflow */
> +#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5)
> +/** Host Interrupt Status bit : I/O read FIFO underflow */
> +#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6)
> +/** Host Interrupt Status bit : Data write FIFO overflow */
> +#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7)
> +/** Host Interrupt Status bit : Data read FIFO underflow */
> +#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8)
> +/** Host Interrupt Status bit : Command write FIFO overflow */
> +#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9)
> +/** Host Interrupt Status bit : Command read FIFO underflow */
> +#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10)
> +
> +/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/
> +/** Host Interrupt Status Mask bit : Tx download ready */
> +#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0)
> +/** Host Interrupt Status Mask bit : Rx upload ready */
> +#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1)
> +/** Host Interrupt Status Mask bit : Command download ready */
> +#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2)
> +/** Host Interrupt Status Mask bit : Card event */
> +#define IF_SPI_HISM_CARDEVENT (1<<3)
> +/** Host Interrupt Status Mask bit : Command upload ready */
> +#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4)
> +/** Host Interrupt Status Mask bit : I/O write FIFO overflow */
> +#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5)
> +/** Host Interrupt Status Mask bit : I/O read FIFO underflow */
> +#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6)
> +/** Host Interrupt Status Mask bit : Data write FIFO overflow */
> +#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7)
> +/** Host Interrupt Status Mask bit : Data write FIFO underflow */
> +#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8)
> +/** Host Interrupt Status Mask bit : Command write FIFO overflow */
> +#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9)
> +/** Host Interrupt Status Mask bit : Command write FIFO underflow */
> +#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10)
> +
> +/***************** IF_SPI_SPU_BUS_MODE_REG *****************/
> +/* SCK edge on which the WLAN module outputs data on MISO */
> +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8
> +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0
> +
> +/* In a SPU read operation, there is a delay between writing the SPU
> + * register name and getting back data from the WLAN module.
> + * This can be specified in terms of nanoseconds or in terms of dummy
> + * clock cycles which the master must output before receiving a response. */
> +#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4
> +#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0
> +
> +/* Some different modes of SPI operation */
> +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00
> +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01
> +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02
> +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03
> +
> +#endif
> diff --git a/include/linux/spi/libertas_spi.h b/include/linux/spi/libertas_spi.h
> new file mode 100644
> index 0000000..ada71b4
> --- /dev/null
> +++ b/include/linux/spi/libertas_spi.h
> @@ -0,0 +1,25 @@
> +/*
> + * board-specific data for the libertas_spi driver.
> + *
> + * Copyright 2008 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or (at
> + * your option) any later version.
> + */
> +#ifndef _LIBERTAS_SPI_H_
> +#define _LIBERTAS_SPI_H_
> +struct libertas_spi_platform_data {
> + /* There are two ways to read data from the WLAN module's SPI
> + * interface. Setting 0 or 1 here controls which one is used.
> + *
> + * Usually you want to set use_dummy_writes = 1.
> + * However, if that doesn't work or if you are using a slow SPI clock
> + * speed, you may want to use 0 here. */
> + u16 use_dummy_writes;
> +
> + /* GPIO number to use as chip select */
> + u16 gpio_cs;
> +};
> +#endif
More information about the libertas-dev
mailing list