/*
* 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 "if_sdio.h"
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) },
{ /* 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 = 0x04,
.helper = "sd8385_helper.bin",
.firmware = "sd8385.bin",
},
{
/* 8686 */
.model = 0x0B,
.helper = "sd8686_helper.bin",
.firmware = "sd8686.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;
const char *helper;
const char *firmware;
u8 buffer[65536];
spinlock_t lock;
struct if_sdio_packet *packets;
struct work_struct packet_worker;
};
/********************************************************************/
/* I/O */
/********************************************************************/
static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
{
int ret, reg;
u16 scratch;
if (card->model == 0x04)
reg = IF_SDIO_SCRATCH_OLD;
else
reg = IF_SDIO_SCRATCH;
scratch = sdio_readb(card->func, reg, &ret);
if (!ret)
scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;
if (err)
*err = ret;
if (ret)
return 0xffff;
return scratch;
}
static int if_sdio_handle_cmd(struct if_sdio_card *card,
u8 *buffer, unsigned size)
{
struct lbs_private *priv = card->priv;
int ret;
unsigned long flags;
u8 i;
lbs_deb_enter(LBS_DEB_SDIO);
if (size > LBS_CMD_BUFFER_SIZE) {
lbs_deb_sdio("response packet too large (%d bytes)\n",
(int)size);
ret = -E2BIG;
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] = size;
memcpy(priv->resp_buf[i], buffer, size);
lbs_notify_command_response(priv, i);
spin_unlock_irqrestore(&card->priv->driver_lock, flags);
ret = 0;
out:
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
return ret;
}
static int if_sdio_handle_data(struct if_sdio_card *card,
u8 *buffer, unsigned size)
{
int ret;
struct