/*
* linux/drivers/net/wireless/libertas/if_spi.c
*
* Driver for Marvell SPI WLAN cards.
*
* Copyright 2008 Analog Devices Inc.
*
* Authors:
* Andrey Yurovsky <andrey@cozybit.com>
* Colin McCabe <colin@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/jiffies.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/semaphore.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_card {
struct spi_device *spi;
struct lbs_private *priv;
struct libertas_spi_platform_data *pdata;
char helper_fw_name[IF_SPI_FW_NAME_MAX];
char main_fw_name[IF_SPI_FW_NAME_MAX];
/* The card ID and card revision, as reported by the hardware. */
u16 card_id;
u8 card_rev;
/* The last time that we initiated an SPU operation */
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];
};
static void free_if_spi_card(struct if_spi_card *card)
{
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.
*
*/
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);
}
}
static void spu_transaction_finish(struct if_spi_card *card)
{
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;
__le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK);
struct spi_message m;
struct spi_transfer reg_trans;
struct spi_transfer data_trans;
spi_message_init(&m);
memset(®_trans, 0, sizeof(reg_trans));
memset(&data_trans, 0, sizeof(data_trans));
/* 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 */
reg_trans.tx_buf = ®_out;
reg_trans.len = sizeof(reg_out);
data_trans.tx_buf = buf;
data_trans.len = len;
spi_message_add_tail(®_trans, &m);
spi_message_add_tail(&data_trans, &m);
err = spi_sync(card->spi, &m);
spu_transaction_finish(card);
return err;
}
static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val)
{
__le16 buff;
buff = cpu_to_le16(val);
return spu_write(card, reg, (u8 *)&buff, sizeof(u16));
}
static inline int spu_reg_is_port_reg(u16 reg)
{