Return-path: Received: from mx2.redhat.com ([66.187.237.31]:38695 "EHLO mx2.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754241AbZALTiQ (ORCPT ); Mon, 12 Jan 2009 14:38:16 -0500 Subject: Re: [PATCH v3] libertas: if_spi, driver for libertas GSPI devices From: Dan Williams To: Colin McCabe Cc: linux-wireless@vger.kernel.org, Andrey Yurovsky , libertas-dev@lists.infradead.org In-Reply-To: <1231541889-7999-1-git-send-email-colin@cozybit.com> References: <1231541889-7999-1-git-send-email-colin@cozybit.com> Content-Type: text/plain Date: Mon, 12 Jan 2009 14:36:58 -0500 Message-Id: <1231789019.28887.8.camel@localhost.localdomain> (sfid-20090112_203823_925026_8C5CE85D) Mime-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org List-ID: On Fri, 2009-01-09 at 14:58 -0800, Colin McCabe wrote: > Add initial support for libertas devices using a GSPI interface. This has > been tested with the 8686. > > GSPI is intended to be used on embedded systems. Board-specific parameters are > required (see libertas_spi.h). > > Thanks to everyone who took a look at the earlier versions of the patch. Thanks! Looks good. Acked-by: Dan Williams > Changes in v3: > * Get rid of out-of-date TODO comment about if_spi_e2h > * Look for firmware images in libertas/gspi8XXX_hlp.bin and > libertas/gspi8XXX.bin > * Fix spi_read / spi_write error handling in spu_read / spu_write > * Consolidate multiple spi_read / spi_write operations into one to > improve performance > * Add dependency on GENERIC_GPIO > > Changes in v2: > * Store the host IRQ setting in spi->irq rather than creating a new variable > for this purpose. > * Add TODO comment to declaration of card->gpio_cs. > (We do intend to get rid of gpio_cs eventually, but it will require some > changes to the generic SPI layer, which could take a while.) > * When loading the module, check to see if the firmware is already loaded. If > so, skip firmware load. > * Speed up module load time slightly. > * Add if_spi_e2h to handle libertas card events. > * Include relevant Kconfig change. > > Cheers. > Colin > Signed-off-by: Colin McCabe > Signed-off-by: Andrey Yurovsky > --- > 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 | 1204 ++++++++++++++++++++++++++++++++ > drivers/net/wireless/libertas/if_spi.h | 208 ++++++ > include/linux/spi/libertas_spi.h | 25 + > 6 files changed, 1447 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 && GENERIC_GPIO > + ---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..86b984e > --- /dev/null > +++ b/drivers/net/wireless/libertas/if_spi.c > @@ -0,0 +1,1204 @@ > +/* > + * linux/drivers/net/wireless/libertas/if_spi.c > + * > + * Driver for Marvell SPI WLAN cards. > + * > + * Copyright 2008 Analog Devices Inc. > + * > + * Authors: > + * Andrey Yurovsky > + * Colin McCabe > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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; > + > + 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 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; > + > + err = spi_write(card->spi, buf, len); > + > +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 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) > + goto out; > + > + 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; > + } > + } else { > + /* Busy-wait while the SPU fills the FIFO */ > + ndelay(100 + (delay * 10)); > + } > + > + /* read in data */ > + err = spi_read(card->spi, buf, len); > + > +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); > + > +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, "libertas/gspi%d_hlp.bin", > + chip_id_to_device_name[i].name); > + snprintf(main_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d.bin", > + chip_id_to_device_name[i].name); > + 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 , " > + "Colin McCabe "); > +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 > + * Colin McCabe > + * > + * 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