/*
* linux/drivers/net/wireless/libertas/if_sdio.c
*
* Copyright 2007-2008 Pierre Ossman
*
* Inspired by if_cs.c, Copyright 2007 Holger Schurig
*
* 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.
*
* This hardware has more or less no CMD53 support, so all registers
* must be accessed using sdio_readb()/sdio_writeb().
*
* Transfers must be in one transaction or the firmware goes bonkers.
* This means that the transfer must either be small enough to do a
* byte based transfer or it must be padded to a multiple of the
* current block size.
*
* As SDIO is still new to the kernel, it is unfortunately common with
* bugs in the host controllers related to that. One such bug is that
* controllers cannot do transfers that aren't a multiple of 4 bytes.
* If you don't have time to fix the host controller driver, you can
* work around the problem by modifying if_sdio_host_to_card() and
* if_sdio_card_to_host() to pad the data.
*/
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include "host.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
#include "cmd.h"
#include "if_sdio.h"
/* The if_sdio_remove() callback function is called when
* user removes this module from kernel space or ejects
* the card from the slot. The driver handles these 2 cases
* differently for SD8688 combo chip.
* If the user is removing the module, the FUNC_SHUTDOWN
* command for SD8688 is sent to the firmware.
* If the card is removed, there is no need to send this command.
*
* The variable 'user_rmmod' is used to distinguish these two
* scenarios. This flag is initialized as FALSE in case the card
* is removed, and will be set to TRUE for module removal when
* module_exit function is called.
*/
static u8 user_rmmod;
static char *lbs_helper_name = NULL;
module_param_named(helper_name, lbs_helper_name, charp, 0644);
static char *lbs_fw_name = NULL;
module_param_named(fw_name, lbs_fw_name, charp, 0644);
static const struct sdio_device_id if_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
SDIO_DEVICE_ID_MARVELL_8688WLAN) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
struct if_sdio_model {
int model;
const char *helper;
const char *firmware;
};
static struct if_sdio_model if_sdio_models[] = {
{
/* 8385 */
.model = IF_SDIO_MODEL_8385,
.helper = "sd8385_helper.bin",
.firmware = "sd8385.bin",
},
{
/* 8686 */
.model = IF_SDIO_MODEL_8686,
.helper = "sd8686_helper.bin",
.firmware = "sd8686.bin",
},
{
/* 8688 */
.model = IF_SDIO_MODEL_8688,
.helper = "sd8688_helper.bin",
.firmware = "sd8688.bin",
},
};
struct if_sdio_packet {
struct if_sdio_packet *next;
u16 nb;
u8 buffer[0] __attribute__((aligned(4)));
};
struct if_sdio_card {
struct sdio_func *func;
struct lbs_private *priv;
int model;
unsigned long ioport;
unsigned int scratch_reg;
const char *helper;
const char *firmware;
u8 buffer[65536];
spinlock_t lock;
struct if_sdio_packet *packets;
struct workqueue_struct *workqueue;
struct work_struct packet_worker;
u8 rx_unit;
};
/********************************************************************/
/* I/O */
/********************************************************************/
/*
* For SD8385/SD8686, this function reads firmware status after
* the image is downloaded, or reads RX packet length when
* interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
* For SD8688, this function reads firmware status only.
*/
static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
{
int ret;
u16 scratch;
scratch = sdio_readb(card->func, card->scratch_reg, &ret);
if (!ret)
scratch |= sdio_readb(