blob: 8e6a29d9aa2e60e932950177004dfc6ac9d9c18a [file] [log] [blame]
/* tnetw_sdio.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Copyright © Texas Instruments Incorporated (Oct 2005)
* THIS CODE/PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDED BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* This program has been modified from its original operation by Texas
* Instruments Incorporated. These changes are covered under version 2
* of the GNU General Public License, dated June 1991.
*
* Copyright © Google Inc (Feb 2008)
*/
/*-------------------------------------------------------------------*/
#ifdef TIWLAN_MSM7000
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
#include <linux/delay.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include "esta_drv.h"
#include "mmc_omap_api.h"
#include "osApi.h"
/*-------------------------------------------------------------------*/
extern int tiwlan_sdio_init(struct sdio_func *func);
extern int sdio_reset_comm(struct mmc_card *card);
/*-------------------------------------------------------------------*/
static struct sdio_func *tiwlan_func = NULL;
static int sdio_reset_flag = 0;
#define DMA_THRESHOLD_SIZE 64
static void *sdio_dma_ptr = NULL;
#define USE_SKETCHY_WRITES 0
/*-------------------------------------------------------------------*/
void SDIO_SetFunc( struct sdio_func *func )
{
tiwlan_func = func;
}
struct sdio_func *SDIO_GetFunc( void )
{
return tiwlan_func;
}
SDIO_Status SDIO_Init(SDIO_ConfigParams *ConfigParams, SDIO_Handle *Handle)
{
if (Handle == NULL) {
printk(KERN_ERR "Error: SDIO_Init() called with NULL!\n");
return SDIO_FAILURE;
}
*Handle = (SDIO_Handle)SDIO_GetFunc();
if ((*Handle) == NULL) {
printk(KERN_ERR "SDIO_Init() called before init!\n");
return SDIO_FAILURE;
}
if (!sdio_dma_ptr) {
if (!(sdio_dma_ptr = kmalloc(PAGE_SIZE, GFP_KERNEL))) {
printk(KERN_ERR "Failed to alloc DMA bounce buffer\n");
return SDIO_FAILURE;
}
}
return SDIO_SUCCESS;
}
SDIO_Status SDIO_Shutdown(SDIO_Handle Handle)
{
if (sdio_dma_ptr) {
kfree(sdio_dma_ptr);
sdio_dma_ptr = NULL;
}
return SDIO_SUCCESS;
}
SDIO_Status SDIO_Start(SDIO_Handle Handle)
{
struct sdio_func *func = (struct sdio_func *)Handle;
if (func) {
if (sdio_reset_flag) {
sdio_reset_flag = 0;
if (tiwlan_sdio_init(func)) {
printk("TI: tiwlan_sdio_init Error!\n");
return SDIO_FAILURE;
}
}
}
return SDIO_SUCCESS;
}
SDIO_Status SDIO_Reset(SDIO_Handle Handle)
{
struct sdio_func *func = (struct sdio_func *)Handle;
if (func && func->card) {
sdio_release_host(func);
sdio_reset_comm(func->card);
sdio_claim_host(func);
}
return SDIO_SUCCESS;
}
SDIO_Status SDIO_Stop(SDIO_Handle Handle, unsigned long Wait_Window)
{
sdio_reset_flag = 1;
return SDIO_Reset(Handle);
}
static inline int spans_page(void *s, int len)
{
if (((unsigned long) s + len) <= ((((unsigned long) s) & ~(PAGE_SIZE-1)) + PAGE_SIZE))
return 0;
return 1;
}
static void *vmalloc_to_unity(void *a)
{
struct page *pg;
unsigned long virt = (unsigned long) a;
pg = vmalloc_to_page(a);
virt = (unsigned long)page_address(pg) | (virt & (PAGE_SIZE -1));
return (void *)virt;
}
SDIO_Status SDIO_SyncRead(SDIO_Handle Handle, SDIO_Request_t *Req)
{
struct sdio_func *func = (struct sdio_func *)Handle;
int rc;
void *tgt = Req->buffer;
if (Req->buffer_len >= DMA_THRESHOLD_SIZE) {
if (is_vmalloc_addr(tgt)) {
if (!spans_page(tgt, Req->buffer_len)) {
tgt = vmalloc_to_unity(tgt);
dmac_flush_range(Req->buffer,
Req->buffer + Req->buffer_len);
} else
tgt = sdio_dma_ptr;
}
}
if ((rc = sdio_memcpy_fromio(func, tgt, Req->peripheral_addr,
Req->buffer_len))) {
printk(KERN_ERR "%s: failed (%d)\n", __func__, rc);
return SDIO_FAILURE;
}
if (tgt == sdio_dma_ptr)
memcpy(Req->buffer, sdio_dma_ptr, Req->buffer_len);
return SDIO_SUCCESS;
}
SDIO_Status SDIO_SyncWrite(SDIO_Handle Handle, SDIO_Request_t *Req)
{
struct sdio_func *func = (struct sdio_func *)Handle;
int rc;
void *src = Req->buffer;
if (Req->buffer_len >= DMA_THRESHOLD_SIZE) {
#if USE_SKETCHY_WRITES
if (is_vmalloc_addr(src)) {
if (!spans_page(src, Req->buffer_len)) {
src = vmalloc_to_unity(src);
dmac_clean_range(Req->buffer,
Req->buffer + Req->buffer_len);
} else {
#endif
src = sdio_dma_ptr;
memcpy(src, Req->buffer, Req->buffer_len);
#if USE_SKETCHY_WRITES
}
}
#endif
}
rc = sdio_memcpy_toio(func, Req->peripheral_addr, src,
Req->buffer_len);
if (!rc)
return SDIO_SUCCESS;
printk(KERN_ERR "%s: failed (%d)\n", __func__, rc);
return SDIO_FAILURE;
}
#endif