/*
 * SdioDrv.c
 *
 * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name Texas Instruments nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#include <linux/errno.h>
#include <linux/clk.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
#include <plat/hardware.h>
#include <plat/board.h>
#include <plat/clock.h>
#include <plat/dma.h>
#include <plat/io.h>
#include <plat/resource.h>
#define IO_ADDRESS(pa)	OMAP2_L4_IO_ADDRESS(pa)
#else
#include <mach/hardware.h>
#include <mach/board.h>
#include <mach/clock.h>
#include <mach/dma.h>
#include <mach/io.h>
#include <mach/resource.h>
#endif
typedef void *TI_HANDLE;
#include "host_platform.h"
#include "SdioDrvDbg.h"
#include "SdioDrv.h"

/* #define TI_SDIO_DEBUG */

#ifndef CONFIG_MMC_EMBEDDED_SDIO

#define SDIOWQ_NAME			"sdio_wq"

/*
 * HSMMC Address and DMA Settings
 */
static unsigned long TIWLAN_MMC_CONTROLLER = 2; /* MMC3 */
static unsigned long TIWLAN_MMC_CONTROLLER_BASE_ADDR = OMAP_HSMMC3_BASE;
#define TIWLAN_MMC_CONTROLLER_BASE_SIZE	512
#define TIWLAN_MMC_MAX_DMA		8192
static unsigned long TIWLAN_MMC_DMA_TX = OMAP34XX_DMA_MMC3_TX;
static unsigned long TIWLAN_MMC_DMA_RX = OMAP34XX_DMA_MMC3_RX;
static unsigned long OMAP_MMC_IRQ = INT_MMC3_IRQ;

#define OMAP_MMC_MASTER_CLOCK          96000000
/*
 *  HSMMC Host Controller Registers
 */
#define OMAP_HSMMC_SYSCONFIG           0x0010
#define OMAP_HSMMC_SYSSTATUS           0x0014
#define OMAP_HSMMC_CSRE                0x0024
#define OMAP_HSMMC_SYSTEST             0x0028
#define OMAP_HSMMC_CON                 0x002C
#define OMAP_HSMMC_BLK                 0x0104
#define OMAP_HSMMC_ARG                 0x0108
#define OMAP_HSMMC_CMD                 0x010C
#define OMAP_HSMMC_RSP10               0x0110
#define OMAP_HSMMC_RSP32               0x0114
#define OMAP_HSMMC_RSP54               0x0118
#define OMAP_HSMMC_RSP76               0x011C
#define OMAP_HSMMC_DATA                0x0120
#define OMAP_HSMMC_PSTATE              0x0124
#define OMAP_HSMMC_HCTL                0x0128
#define OMAP_HSMMC_SYSCTL              0x012C
#define OMAP_HSMMC_STAT                0x0130
#define OMAP_HSMMC_IE                  0x0134
#define OMAP_HSMMC_ISE                 0x0138
#define OMAP_HSMMC_AC12                0x013C
#define OMAP_HSMMC_CAPA                0x0140
#define OMAP_HSMMC_CUR_CAPA            0x0148
#define OMAP_HSMMC_REV                 0x01FC

#define VS18                           (1 << 26)
#define VS30                           (1 << 25)
#define SRA                            (1 << 24)
#define SDVS18                         (0x5 << 9)
#define SDVS30                         (0x6 << 9)
#define SDVSCLR                        0xFFFFF1FF
#define SDVSDET                        0x00000400
#define SIDLE_MODE                     (0x2 << 3)
#define AUTOIDLE                       0x1
#define SDBP                           (1 << 8)
#define DTO                            0xE
#define ICE                            0x1
#define ICS                            0x2
#define CEN                            (1 << 2)
#define CLKD_MASK                      0x0000FFC0
#define IE_EN_MASK                     0x317F0137
#define INIT_STREAM                    (1 << 1)
#define DP_SELECT                      (1 << 21)
#define DDIR                           (1 << 4)
#define DMA_EN                         0x1
#define MSBS                           (1 << 5)
#define BCE                            (1 << 1)
#define ONE_BIT                        (~(0x2))
#define EIGHT_BIT                      (~(0x20))
#define CC                             0x1
#define TC                             0x02
#define OD                             0x1
#define BRW                            0x400
#define BRR                            0x800
#define BRE                            (1 << 11)
#define BWE                            (1 << 10)
#define SBGR                           (1 << 16)
#define CT                             (1 << 17)
#define SDIO_READ                      (1 << 31)
#define SDIO_BLKMODE                   (1 << 27)
#define OMAP_HSMMC_ERR                 (1 << 15)  /* Any error */
#define OMAP_HSMMC_CMD_TIMEOUT         (1 << 16)  /* Com mand response time-out */
#define OMAP_HSMMC_DATA_TIMEOUT        (1 << 20)  /* Data response time-out */
#define OMAP_HSMMC_CMD_CRC             (1 << 17)  /* Command CRC error */
#define OMAP_HSMMC_DATA_CRC            (1 << 21)  /* Date CRC error */
#define OMAP_HSMMC_CARD_ERR            (1 << 28)  /* Card ERR */
#define OMAP_HSMMC_STAT_CLEAR          0xFFFFFFFF
#define INIT_STREAM_CMD                0x00000000
#define INT_CLEAR                      0x00000000
#define BLK_CLEAR                      0x00000000

/* SCM CONTROL_DEVCONF1 MMC1 overwrite but */

#define MMC1_ACTIVE_OVERWRITE          (1 << 31)

#define sdio_blkmode_regaddr           0x2000
#define sdio_blkmode_mask              0xFF00

#define IO_RW_DIRECT_MASK              0xF000FF00
#define IO_RW_DIRECT_ARG_MASK          0x80001A00

#define RMASK                          (MMC_RSP_MASK | MMC_RSP_CRC)
#define MMC_TIMEOUT_MS                 100 /*on the new 2430 it was 20, i changed back to 100*//* obc */
#define MMCA_VSN_4                     4

#define VMMC1_DEV_GRP                  0x27
#define P1_DEV_GRP                     0x20
#define VMMC1_DEDICATED                0x2A
#define VSEL_3V                        0x02
#define VSEL_18V                       0x00
#define PBIAS_3V                       0x03
#define PBIAS_18V                      0x02
#define PBIAS_LITE                     0x04A0
#define PBIAS_CLR                      0x00

#define OMAP_MMC_REGS_BASE             IO_ADDRESS(TIWLAN_MMC_CONTROLLER_BASE_ADDR)

/*
 * MMC Host controller read/write API's.
 */
#define OMAP_HSMMC_READ_OFFSET(offset) (__raw_readl((OMAP_MMC_REGS_BASE) + (offset)))
#define OMAP_HSMMC_READ(reg)           (__raw_readl((OMAP_MMC_REGS_BASE) + OMAP_HSMMC_##reg))
#define OMAP_HSMMC_WRITE(reg, val)     (__raw_writel((val), (OMAP_MMC_REGS_BASE) + OMAP_HSMMC_##reg))

#define OMAP_HSMMC_SEND_COMMAND(cmd, arg) do \
{ \
	OMAP_HSMMC_WRITE(ARG, arg); \
	OMAP_HSMMC_WRITE(CMD, cmd); \
} while (0)

#define OMAP_HSMMC_CMD52_WRITE     ((SD_IO_RW_DIRECT    << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16))
#define OMAP_HSMMC_CMD52_READ      (((SD_IO_RW_DIRECT   << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) | DDIR)
#define OMAP_HSMMC_CMD53_WRITE     (((SD_IO_RW_EXTENDED << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) | DP_SELECT)
#define OMAP_HSMMC_CMD53_READ      (((SD_IO_RW_EXTENDED << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) | DP_SELECT | DDIR)
#define OMAP_HSMMC_CMD53_READ_DMA  (OMAP_HSMMC_CMD53_READ  | DMA_EN)
#define OMAP_HSMMC_CMD53_WRITE_DMA (OMAP_HSMMC_CMD53_WRITE | DMA_EN)

/* Macros to build commands 52 and 53 in format according to SDIO spec */
#define SDIO_CMD52_READ(v1,v2,v3,v4)        (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_RAWFLAG(v3)| SDIO_ADDRREG(v4))
#define SDIO_CMD52_WRITE(v1,v2,v3,v4,v5)    (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_RAWFLAG(v3)| SDIO_ADDRREG(v4)|(v5))
#define SDIO_CMD53_READ(v1,v2,v3,v4,v5,v6)  (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_BLKM(v3)| SDIO_OPCODE(v4)|SDIO_ADDRREG(v5)|(v6&0x1ff))
#define SDIO_CMD53_WRITE(v1,v2,v3,v4,v5,v6) (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_BLKM(v3)| SDIO_OPCODE(v4)|SDIO_ADDRREG(v5)|(v6&0x1ff))

#define SDIODRV_MAX_LOOPS	50000

#define VMMC2_DEV_GRP		0x2B
#define VMMC2_DEDICATED		0x2E
#define VSEL_S2_18V		0x05
#define LDO_CLR			0x00
#define VSEL_S2_CLR		0x40
#define GPIO_0_BIT_POS		1 << 0
#define GPIO_1_BIT_POS		1 << 1
#define VSIM_DEV_GRP		0x37
#define VSIM_DEDICATED		0x3A
#define TWL4030_MODULE_PM_RECIEVER	0x13

typedef struct OMAP3430_sdiodrv
{
	struct clk    *fclk, *iclk, *dbclk;
	int           ifclks_enabled;
	spinlock_t    clk_lock;	
	int           dma_tx_channel;
	int           dma_rx_channel;
	int           irq;
	void          (*BusTxnCB)(void* BusTxnHandle, int status);
	void*         BusTxnHandle;
	unsigned int  uBlkSize;
	unsigned int  uBlkSizeShift;
	int           async_status;
	int (*wlanDrvIf_pm_resume)(void);
	int (*wlanDrvIf_pm_suspend)(void);
	struct device *dev;
	dma_addr_t dma_read_addr;
	size_t dma_read_size;
	dma_addr_t dma_write_addr;
	size_t dma_write_size;
	struct workqueue_struct *sdio_wq; /* Work Queue */
	struct work_struct sdiodrv_work;
} OMAP3430_sdiodrv_t;

struct omap_hsmmc_regs {
        u32 hctl;
        u32 capa;
        u32 sysconfig;
        u32 ise;
        u32 ie;
        u32 con;
        u32 sysctl;
};
static struct omap_hsmmc_regs hsmmc_ctx;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
static struct platform_device dummy_pdev = {
	.dev = {
		.bus = &platform_bus_type,
	},
};
#endif

#define SDIO_DRIVER_NAME 			"TIWLAN_SDIO"

module_param(g_sdio_debug_level, int, 0644);
MODULE_PARM_DESC(g_sdio_debug_level, "debug level");
int g_sdio_debug_level = SDIO_DEBUGLEVEL_ERR;
EXPORT_SYMBOL(g_sdio_debug_level);

OMAP3430_sdiodrv_t g_drv;

static int sdiodrv_irq_requested = 0;
static int sdiodrv_iclk_got = 0;
static int sdiodrv_fclk_got = 0;

static void sdioDrv_hsmmc_save_ctx(void);
static void sdioDrv_hsmmc_restore_ctx(void);
static void sdiodrv_dma_shutdown(void);

#ifndef TI_SDIO_STANDALONE
void sdio_init( int sdcnum )
{
	if( sdcnum <= 0 )
		return;
	TIWLAN_MMC_CONTROLLER = sdcnum - 1;
	if( sdcnum == 2 ) {
		TIWLAN_MMC_CONTROLLER_BASE_ADDR = OMAP_HSMMC2_BASE;
		TIWLAN_MMC_DMA_TX = OMAP24XX_DMA_MMC2_TX;
		TIWLAN_MMC_DMA_RX = OMAP24XX_DMA_MMC2_RX;
		OMAP_MMC_IRQ = INT_MMC2_IRQ;
	}
	else if( sdcnum == 3 ) {
		TIWLAN_MMC_CONTROLLER_BASE_ADDR	= OMAP_HSMMC3_BASE;
		TIWLAN_MMC_DMA_TX = OMAP34XX_DMA_MMC3_TX;
		TIWLAN_MMC_DMA_RX = OMAP34XX_DMA_MMC3_RX;
		OMAP_MMC_IRQ = INT_MMC3_IRQ;
	}
}
#endif

static void sdioDrv_hsmmc_save_ctx(void)
{
	/* MMC : context save */
	hsmmc_ctx.hctl = OMAP_HSMMC_READ(HCTL);
	hsmmc_ctx.capa = OMAP_HSMMC_READ(CAPA);
	hsmmc_ctx.sysconfig = OMAP_HSMMC_READ(SYSCONFIG);
	hsmmc_ctx.ise = OMAP_HSMMC_READ(ISE);
	hsmmc_ctx.ie = OMAP_HSMMC_READ(IE);
	hsmmc_ctx.con = OMAP_HSMMC_READ(CON);
	hsmmc_ctx.sysctl = OMAP_HSMMC_READ(SYSCTL);
	OMAP_HSMMC_WRITE(ISE, 0);
	OMAP_HSMMC_WRITE(IE, 0);
}

static void sdioDrv_hsmmc_restore_ctx(void)
{
        /* MMC : context restore */
        OMAP_HSMMC_WRITE(HCTL, hsmmc_ctx.hctl);
        OMAP_HSMMC_WRITE(CAPA, hsmmc_ctx.capa);
        OMAP_HSMMC_WRITE(SYSCONFIG, hsmmc_ctx.sysconfig);
        OMAP_HSMMC_WRITE(CON, hsmmc_ctx.con);
        OMAP_HSMMC_WRITE(ISE, hsmmc_ctx.ise);
        OMAP_HSMMC_WRITE(IE, hsmmc_ctx.ie);
        OMAP_HSMMC_WRITE(SYSCTL, hsmmc_ctx.sysctl);
        OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) | SDBP);
}

void sdiodrv_task(struct work_struct *unused)
{
	PDEBUG("sdiodrv_tasklet()\n");

	if (g_drv.dma_read_addr != 0) {
		dma_unmap_single(g_drv.dev, g_drv.dma_read_addr, g_drv.dma_read_size, DMA_FROM_DEVICE);
		g_drv.dma_read_addr = 0;
		g_drv.dma_read_size = 0;
	}
	
	if (g_drv.dma_write_addr != 0) {
		dma_unmap_single(g_drv.dev, g_drv.dma_write_addr, g_drv.dma_write_size, DMA_TO_DEVICE);
		g_drv.dma_write_addr = 0;
		g_drv.dma_write_size = 0;
	}

	if (g_drv.BusTxnCB != NULL) {
		g_drv.BusTxnCB(g_drv.BusTxnHandle, g_drv.async_status);
	}
}

irqreturn_t sdiodrv_irq(int irq, void *drv)
{
	int status;

	PDEBUG("sdiodrv_irq()\n");

	status = OMAP_HSMMC_READ(STAT);
	OMAP_HSMMC_WRITE(ISE, 0);
	g_drv.async_status = status & (OMAP_HSMMC_ERR);
	if (g_drv.async_status) {
		PERR("sdiodrv_irq: ERROR in STAT = 0x%x\n", status);
	}
	queue_work(g_drv.sdio_wq, &g_drv.sdiodrv_work);
	return IRQ_HANDLED;
}

void sdiodrv_dma_read_cb(int lch, u16 ch_status, void *data)
{
	PDEBUG("sdiodrv_dma_read_cb() channel=%d status=0x%x\n", lch, (int)ch_status);

	g_drv.async_status = ch_status & (1 << 7);

	queue_work(g_drv.sdio_wq, &g_drv.sdiodrv_work);
	sdiodrv_dma_shutdown();
}

void sdiodrv_dma_write_cb(int lch, u16 ch_status, void *data)
{
	sdiodrv_dma_shutdown();
}

int sdiodrv_dma_init(void)
{
	int rc;

	rc = omap_request_dma(TIWLAN_MMC_DMA_TX, "SDIO WRITE", sdiodrv_dma_write_cb, &g_drv, &g_drv.dma_tx_channel);
	if (rc != 0) {
		PERR("sdiodrv_dma_init() omap_request_dma(TIWLAN_MMC_DMA_TX) FAILED\n");
		goto out;
	}

	rc = omap_request_dma(TIWLAN_MMC_DMA_RX, "SDIO READ", sdiodrv_dma_read_cb, &g_drv, &g_drv.dma_rx_channel);
	if (rc != 0) {
		PERR("sdiodrv_dma_init() omap_request_dma(TIWLAN_MMC_DMA_RX) FAILED\n");
		goto freetx;
	}

	omap_set_dma_src_params(g_drv.dma_rx_channel,
  							0,			// src_port is only for OMAP1
  							OMAP_DMA_AMODE_CONSTANT,
  							(TIWLAN_MMC_CONTROLLER_BASE_ADDR) + OMAP_HSMMC_DATA, 0, 0);
  
	omap_set_dma_dest_params(g_drv.dma_tx_channel,
							0,			// dest_port is only for OMAP1
  	  						OMAP_DMA_AMODE_CONSTANT,
  	  						(TIWLAN_MMC_CONTROLLER_BASE_ADDR) + OMAP_HSMMC_DATA, 0, 0);

	return 0;

freetx:
	omap_free_dma(g_drv.dma_tx_channel);
out:
	return rc;
}

static void sdiodrv_dma_shutdown(void)
{
	omap_free_dma(g_drv.dma_tx_channel);
	omap_free_dma(g_drv.dma_rx_channel);
} /* sdiodrv_dma_shutdown() */

static u32 sdiodrv_poll_status(u32 reg_offset, u32 stat, unsigned int msecs)
{
	u32 status=0, loops=0;

	do
	{
		status = OMAP_HSMMC_READ_OFFSET(reg_offset);
		if(( status & stat))
		{
			break;
		}
	} while (loops++ < SDIODRV_MAX_LOOPS);

	return status;
} /* sdiodrv_poll_status */

void dumpreg(void)
{
	printk(KERN_ERR "\n MMCHS_SYSCONFIG   for mmc3 = %x  ", omap_readl( 0x480AD010 ));
	printk(KERN_ERR "\n MMCHS_SYSSTATUS   for mmc3 = %x  ", omap_readl( 0x480AD014 ));
	printk(KERN_ERR "\n MMCHS_CSRE	      for mmc3 = %x  ", omap_readl( 0x480AD024 ));
	printk(KERN_ERR "\n MMCHS_SYSTEST     for mmc3 = %x  ", omap_readl( 0x480AD028 ));
	printk(KERN_ERR "\n MMCHS_CON         for mmc3 = %x  ", omap_readl( 0x480AD02C ));
	printk(KERN_ERR "\n MMCHS_PWCNT       for mmc3 = %x  ", omap_readl( 0x480AD030 ));
	printk(KERN_ERR "\n MMCHS_BLK         for mmc3 = %x  ", omap_readl( 0x480AD104 ));
	printk(KERN_ERR "\n MMCHS_ARG         for mmc3 = %x  ", omap_readl( 0x480AD108 ));
	printk(KERN_ERR "\n MMCHS_CMD         for mmc3 = %x  ", omap_readl( 0x480AD10C ));
	printk(KERN_ERR "\n MMCHS_RSP10       for mmc3 = %x  ", omap_readl( 0x480AD110 ));
	printk(KERN_ERR "\n MMCHS_RSP32       for mmc3 = %x  ", omap_readl( 0x480AD114 ));
	printk(KERN_ERR "\n MMCHS_RSP54       for mmc3 = %x  ", omap_readl( 0x480AD118 ));
	printk(KERN_ERR "\n MMCHS_RSP76       for mmc3 = %x  ", omap_readl( 0x480AD11C ));
	printk(KERN_ERR "\n MMCHS_DATA        for mmc3 = %x  ", omap_readl( 0x480AD120 ));
	printk(KERN_ERR "\n MMCHS_PSTATE      for mmc3 = %x  ", omap_readl( 0x480AD124 ));
	printk(KERN_ERR "\n MMCHS_HCTL        for mmc3 = %x  ", omap_readl( 0x480AD128 ));
	printk(KERN_ERR "\n MMCHS_SYSCTL      for mmc3 = %x  ", omap_readl( 0x480AD12C ));
	printk(KERN_ERR "\n MMCHS_STAT        for mmc3 = %x  ", omap_readl( 0x480AD130 ));
	printk(KERN_ERR "\n MMCHS_IE          for mmc3 = %x  ", omap_readl( 0x480AD134 ));
	printk(KERN_ERR "\n MMCHS_ISE         for mmc3 = %x  ", omap_readl( 0x480AD138 ));
	printk(KERN_ERR "\n MMCHS_AC12        for mmc3 = %x  ", omap_readl( 0x480AD13C ));
	printk(KERN_ERR "\n MMCHS_CAPA        for mmc3 = %x  ", omap_readl( 0x480AD140 ));
	printk(KERN_ERR "\n MMCHS_CUR_CAPA    for mmc3 = %x  ", omap_readl( 0x480AD148 ));
}

//cmd flow p. 3609 obc
static int sdiodrv_send_command(u32 cmdreg, u32 cmdarg)
{
	OMAP_HSMMC_WRITE(STAT, OMAP_HSMMC_STAT_CLEAR);
	OMAP_HSMMC_SEND_COMMAND(cmdreg, cmdarg);

	return sdiodrv_poll_status(OMAP_HSMMC_STAT, CC, MMC_TIMEOUT_MS);
} /* sdiodrv_send_command() */

/*
 *  Disable clock to the card
 */
static void OMAP3430_mmc_stop_clock(void)
{
	OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) & ~CEN);
	if ((OMAP_HSMMC_READ(SYSCTL) & CEN) != 0x0)
    {
		PERR("MMC clock not stoped, clock freq can not be altered\n");
    }
} /* OMAP3430_mmc_stop_clock */

/*
 *  Reset the SD system
 */
int OMAP3430_mmc_reset(void)
{
	int status, loops=0;
	//p. 3598 - need to set SOFTRESET to 0x1 0bc
	OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) | SRA);
	while ((status = OMAP_HSMMC_READ(SYSCTL) &  SRA) && loops++ < SDIODRV_MAX_LOOPS);
	if (status & SRA)
	{
	    PERR("OMAP3430_mmc_reset() MMC reset FAILED!! status=0x%x\n",status);
	}

	return status;

} /* OMAP3430_mmc_reset */

//p. 3611
static void OMAP3430_mmc_set_clock(unsigned int clock, OMAP3430_sdiodrv_t *host)
{
	u16           dsor = 0;
	unsigned long regVal;
	int           status;

	PDEBUG("OMAP3430_mmc_set_clock(%d)\n",clock);
	if (clock) {
		/* Enable MMC_SD_CLK */
		dsor = OMAP_MMC_MASTER_CLOCK / clock;
		if (dsor < 1) {
			dsor = 1;
		}
		if (OMAP_MMC_MASTER_CLOCK / dsor > clock) {
			dsor++;
		}
		if (dsor > 250) {
			dsor = 250;
		}
	}
	OMAP3430_mmc_stop_clock();
	regVal = OMAP_HSMMC_READ(SYSCTL);
	regVal = regVal & ~(CLKD_MASK);//p. 3652
	regVal = regVal | (dsor << 6);
	regVal = regVal | (DTO << 16);//data timeout
	OMAP_HSMMC_WRITE(SYSCTL, regVal);
	OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) | ICE);//internal clock enable. obc not mentioned in the spec
	/* 
     * wait till the the clock is stable (ICS) bit is set
	 */
	status  = sdiodrv_poll_status(OMAP_HSMMC_SYSCTL, ICS, MMC_TIMEOUT_MS);
	if(!(status & ICS)) {
	    PERR("OMAP3430_mmc_set_clock() clock not stable!! status=0x%x\n",status);
	}
	/* 
	 * Enable clock to the card
	 */
	OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) | CEN);

} /* OMAP3430_mmc_set_clock() */

static void sdiodrv_free_resources(void)
{
	if(g_drv.ifclks_enabled) {
		sdioDrv_clk_disable();
	}

	if (sdiodrv_fclk_got) {
		clk_put(g_drv.fclk);
		sdiodrv_fclk_got = 0;
	}

	if (sdiodrv_iclk_got) {
		clk_put(g_drv.iclk);
		sdiodrv_iclk_got = 0;
	}

        if (sdiodrv_irq_requested) {
                free_irq(OMAP_MMC_IRQ, &g_drv);
                sdiodrv_irq_requested = 0;
        }
}

int sdioDrv_InitHw(void)
{
	return 0;
} /* sdioDrv_InitHw */

void sdiodrv_shutdown(void)
{
	PDEBUG("entering %s()\n" , __FUNCTION__ );

	sdiodrv_free_resources();

	PDEBUG("exiting %s\n", __FUNCTION__);
} /* sdiodrv_shutdown() */

static int sdiodrv_send_data_xfer_commad(u32 cmd, u32 cmdarg, int length, u32 buffer_enable_status, unsigned int bBlkMode)
{
    int status;

	PDEBUG("%s() writing CMD 0x%x ARG 0x%x\n",__FUNCTION__, cmd, cmdarg);

    /* block mode */
	if(bBlkMode) {
        /* 
         * Bits 31:16 of BLK reg: NBLK Blocks count for current transfer.
         *                        in case of Block MOde the lenght is treated here as number of blocks 
         *                        (and not as a length).
         * Bits 11:0 of BLK reg: BLEN Transfer Block Size. in case of block mode set that field to block size. 
         */
        OMAP_HSMMC_WRITE(BLK, (length << 16) | (g_drv.uBlkSize << 0));

        /*
         * In CMD reg:
         * BCE: Block Count Enable
         * MSBS: Multi/Single block select
         */
        cmd |= MSBS | BCE ;
	} else {
        OMAP_HSMMC_WRITE(BLK, length);
    }

    status = sdiodrv_send_command(cmd, cmdarg);
	if(!(status & CC)) {
	    PERR("sdiodrv_send_data_xfer_commad() SDIO Command error! STAT = 0x%x\n", status);
	    return 0;
	}
	PDEBUG("%s() length = %d(%dw) BLK = 0x%x\n",
		   __FUNCTION__, length,((length + 3) >> 2), OMAP_HSMMC_READ(BLK));

    return sdiodrv_poll_status(OMAP_HSMMC_PSTATE, buffer_enable_status, MMC_TIMEOUT_MS);

} /* sdiodrv_send_data_xfer_commad() */

int sdiodrv_data_xfer_sync(u32 cmd, u32 cmdarg, void *data, int length, u32 buffer_enable_status)
{
    u32 buf_start, buf_end, data32;
	int status;

    status = sdiodrv_send_data_xfer_commad(cmd, cmdarg, length, buffer_enable_status, 0);
	if(!(status & buffer_enable_status)) 
    {
	    PERR("sdiodrv_data_xfer_sync() buffer disabled! length = %d BLK = 0x%x PSTATE = 0x%x\n", 
			   length, OMAP_HSMMC_READ(BLK), status);
	    return -1;
	}
	buf_end = (u32)data+(u32)length;

	//obc need to check BRE/BWE every time, see p. 3605
	/*
	 * Read loop 
	 */
	if (buffer_enable_status == BRE)
	{
	  if (((u32)data & 3) == 0) /* 4 bytes aligned */
	  {
		for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long))
		{
		  *((unsigned long*)(data)) = OMAP_HSMMC_READ(DATA);
		}
	  }
	  else                      /* 2 bytes aligned */
	  {
		for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long))
		{
		  data32 = OMAP_HSMMC_READ(DATA);
		  *((unsigned short *)data)     = (unsigned short)data32;
		  *((unsigned short *)data + 1) = (unsigned short)(data32 >> 16);
		}
	  }
	}
	/*
	 * Write loop 
	 */
	else
	{
	  if (((u32)data & 3) == 0) /* 4 bytes aligned */
	  {
		for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long))
		{
		  OMAP_HSMMC_WRITE(DATA,*((unsigned long*)(data)));
		}
	  }
	  else                      /* 2 bytes aligned */
	  {
		for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long))
		{
		  OMAP_HSMMC_WRITE(DATA,*((unsigned short*)data) | *((unsigned short*)data+1) << 16 );
		}

	  }
	}
	status  = sdiodrv_poll_status(OMAP_HSMMC_STAT, TC, MMC_TIMEOUT_MS);
	if(!(status & TC)) 
	{
	    PERR("sdiodrv_data_xfer_sync() transfer error! STAT = 0x%x\n", status);
	    return -1;
	}

	return 0;

} /* sdiodrv_data_xfer_sync() */

int sdioDrv_ConnectBus (void *       fCbFunc,
                        void *       hCbArg,
                        unsigned int uBlkSizeShift,
                        unsigned int  uSdioThreadPriority)
{
	g_drv.BusTxnCB      = fCbFunc;
	g_drv.BusTxnHandle  = hCbArg;
	g_drv.uBlkSizeShift = uBlkSizeShift;  
	g_drv.uBlkSize      = 1 << uBlkSizeShift;

	INIT_WORK(&g_drv.sdiodrv_work, sdiodrv_task);

	return sdioDrv_InitHw ();
}

/*--------------------------------------------------------------------------------------*/

int sdioDrv_DisconnectBus (void)
{
	/* Disable clocks to handle driver stop command */
	sdioDrv_clk_disable();
	return 0;
}

//p.3609 cmd flow
int sdioDrv_ExecuteCmd (unsigned int uCmd, 
                        unsigned int uArg, 
                        unsigned int uRespType, 
                        void *       pResponse, 
                        unsigned int uLen)
{
	unsigned int uCmdReg   = 0;
	unsigned int uStatus   = 0;
	unsigned int uResponse = 0;

	PDEBUG("sdioDrv_ExecuteCmd() starting cmd %02x arg %08x\n", (int)uCmd, (int)uArg);

	sdioDrv_clk_enable(); /* To make sure we have clocks enable */

	uCmdReg = (uCmd << 24) | (uRespType << 16) ;

	uStatus = sdiodrv_send_command(uCmdReg, uArg);

	if (!(uStatus & CC)) 
	{
	    PERR("sdioDrv_ExecuteCmd() SDIO Command error status = 0x%x\n", uStatus);
	    return -1;
	}
	if ((uLen > 0) && (uLen <= 4))/*obc - Len > 4 ? shouldn't read anything ? */
	{
	    uResponse = OMAP_HSMMC_READ(RSP10);
		memcpy (pResponse, (char *)&uResponse, uLen);
		PDEBUG("sdioDrv_ExecuteCmd() response = 0x%x\n", uResponse);
	}
	return 0;
}

/*--------------------------------------------------------------------------------------*/

int sdioDrv_ReadSync (unsigned int uFunc, 
                      unsigned int uHwAddr, 
                      void *       pData, 
                      unsigned int uLen,
                      unsigned int bIncAddr,
                      unsigned int bMore)
{
	unsigned int uCmdArg;
	int          iStatus;

//	printk(KERN_INFO "in sdioDrv_ReadSync\n");
	uCmdArg = SDIO_CMD53_READ(0, uFunc, 0, bIncAddr, uHwAddr, uLen);

	iStatus = sdiodrv_data_xfer_sync(OMAP_HSMMC_CMD53_READ, uCmdArg, pData, uLen, BRE);
	if (iStatus != 0) {
		PERR("sdioDrv_ReadSync() FAILED!!\n");
	}
#ifdef TI_SDIO_DEBUG
	if (uLen == 1)
		printk(KERN_INFO "R53: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)(*(char *)pData));
	else if (uLen == 2)
		printk(KERN_INFO "R53: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)(*(short *)pData));
	else if (uLen == 4)
		printk(KERN_INFO "R53: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)(*(long *)pData));
	else
		printk(KERN_INFO "R53: [0x%x](%u)\n", uHwAddr, uLen);
#endif
	return iStatus;
}

/*--------------------------------------------------------------------------------------*/
int sdioDrv_ReadAsync (unsigned int uFunc, 
                       unsigned int uHwAddr, 
                       void *       pData, 
                       unsigned int uLen, 
                       unsigned int bBlkMode,
                       unsigned int bIncAddr,
                       unsigned int bMore)
{
	int          iStatus;
	unsigned int uCmdArg;
	unsigned int uNumBlks;
	unsigned int uDmaBlockCount;
	unsigned int uNumOfElem;
	dma_addr_t dma_bus_address;

#ifdef TI_SDIO_DEBUG
	printk(KERN_INFO "R53: [0x%x](%u) F[%d]\n", uHwAddr, uLen, uFunc);
#endif

	//printk(KERN_INFO "in sdioDrv_ReadAsync\n");

    if (bBlkMode)
    {
        /* For block mode use number of blocks instead of length in bytes */
        uNumBlks = uLen >> g_drv.uBlkSizeShift;
        uDmaBlockCount = uNumBlks;
        /* due to the DMA config to 32Bit per element (OMAP_DMA_DATA_TYPE_S32) the division is by 4 */ 
        uNumOfElem = g_drv.uBlkSize >> 2;
    }
    else
    {	
        uNumBlks = uLen;
        uDmaBlockCount = 1;
        uNumOfElem = (uLen + 3) >> 2;
    }

    uCmdArg = SDIO_CMD53_READ(0, uFunc, bBlkMode, bIncAddr, uHwAddr, uNumBlks);

    iStatus = sdiodrv_send_data_xfer_commad(OMAP_HSMMC_CMD53_READ_DMA, uCmdArg, uNumBlks, BRE, bBlkMode);

    if (!(iStatus & BRE)) 
    {
        PERR("sdioDrv_ReadAsync() buffer disabled! length = %d BLK = 0x%x PSTATE = 0x%x, BlkMode = %d\n", 
              uLen, OMAP_HSMMC_READ(BLK), iStatus, bBlkMode);
	goto err;
    }

	sdiodrv_dma_init();

	PDEBUG("sdiodrv_read_async() dma_ch=%d \n",g_drv.dma_rx_channel);

	dma_bus_address = dma_map_single(g_drv.dev, pData, uLen, DMA_FROM_DEVICE);
	if (!dma_bus_address) {
		PERR("sdioDrv_ReadAsync: dma_map_single failed\n");
		goto err;
	}		

	if (g_drv.dma_read_addr != 0) {
		printk(KERN_ERR "sdioDrv_ReadAsync: previous DMA op is not finished!\n");
		BUG();
	}
	
	g_drv.dma_read_addr = dma_bus_address;
	g_drv.dma_read_size = uLen;

	omap_set_dma_dest_params    (g_drv.dma_rx_channel,
									0,			// dest_port is only for OMAP1
									OMAP_DMA_AMODE_POST_INC,
									dma_bus_address,
									0, 0);

	omap_set_dma_transfer_params(g_drv.dma_rx_channel, OMAP_DMA_DATA_TYPE_S32, uNumOfElem , uDmaBlockCount , OMAP_DMA_SYNC_FRAME, TIWLAN_MMC_DMA_RX, OMAP_DMA_SRC_SYNC);

	omap_start_dma(g_drv.dma_rx_channel);

	/* Continued at sdiodrv_irq() after DMA transfer is finished */
#ifdef TI_SDIO_DEBUG
	printk(KERN_INFO "R53: [0x%x](%u) (A)\n", uHwAddr, uLen);
#endif
	return 0;
err:
	return -1;

}


/*--------------------------------------------------------------------------------------*/

int sdioDrv_WriteSync (unsigned int uFunc, 
                       unsigned int uHwAddr, 
                       void *       pData, 
                       unsigned int uLen,
                       unsigned int bIncAddr,
                       unsigned int bMore)
{
	unsigned int uCmdArg;
	int          iStatus;

//	printk(KERN_INFO "in sdioDrv_WriteSync\n");

	uCmdArg = SDIO_CMD53_WRITE(1, uFunc, 0, bIncAddr, uHwAddr, uLen);

	iStatus = sdiodrv_data_xfer_sync(OMAP_HSMMC_CMD53_WRITE, uCmdArg, pData, uLen, BWE);
	if (iStatus != 0)
	{
		PERR("sdioDrv_WriteSync() FAILED!!\n");
	}
#ifdef TI_SDIO_DEBUG
	if (uLen == 1)
		printk(KERN_INFO "W53: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)(*(char *)pData));
	else if (uLen == 2)
		printk(KERN_INFO "W53: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)(*(short *)pData));
	else if (uLen == 4)
		printk(KERN_INFO "W53: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)(*(long *)pData));
	else
		printk(KERN_INFO "W53: [0x%x](%u)\n", uHwAddr, uLen);
#endif
	return iStatus;
}

/*--------------------------------------------------------------------------------------*/
int sdioDrv_WriteAsync (unsigned int uFunc, 
                        unsigned int uHwAddr, 
                        void *       pData, 
                        unsigned int uLen, 
                        unsigned int bBlkMode,
                        unsigned int bIncAddr,
                        unsigned int bMore)
{
	int          iStatus;
	unsigned int uCmdArg;
	unsigned int uNumBlks;
	unsigned int uDmaBlockCount;
	unsigned int uNumOfElem;
	dma_addr_t dma_bus_address;

#ifdef TI_SDIO_DEBUG
	printk(KERN_INFO "W53: [0x%x](%u) F[%d] B[%d] I[%d]\n", uHwAddr, uLen, uFunc, bBlkMode, bIncAddr);
#endif

//	printk(KERN_INFO "in sdioDrv_WriteAsync\n");
    if (bBlkMode)
    {
        /* For block mode use number of blocks instead of length in bytes */
        uNumBlks = uLen >> g_drv.uBlkSizeShift;
        uDmaBlockCount = uNumBlks;
        /* due to the DMA config to 32Bit per element (OMAP_DMA_DATA_TYPE_S32) the division is by 4 */ 
        uNumOfElem = g_drv.uBlkSize >> 2;
    }
    else
    {
        uNumBlks = uLen;
        uDmaBlockCount = 1;
        uNumOfElem = (uLen + 3) >> 2;
    }

    uCmdArg = SDIO_CMD53_WRITE(1, uFunc, bBlkMode, bIncAddr, uHwAddr, uNumBlks);

    iStatus = sdiodrv_send_data_xfer_commad(OMAP_HSMMC_CMD53_WRITE_DMA, uCmdArg, uNumBlks, BWE, bBlkMode);
    if (!(iStatus & BWE)) 
    {
        PERR("sdioDrv_WriteAsync() buffer disabled! length = %d, BLK = 0x%x, Status = 0x%x\n", 
             uLen, OMAP_HSMMC_READ(BLK), iStatus);
	goto err;
    }

	OMAP_HSMMC_WRITE(ISE, TC);

	sdiodrv_dma_init();

	dma_bus_address = dma_map_single(g_drv.dev, pData, uLen, DMA_TO_DEVICE);
	if (!dma_bus_address) {
		PERR("sdioDrv_WriteAsync: dma_map_single failed\n");
		goto err;
	}

	if (g_drv.dma_write_addr != 0) {
		PERR("sdioDrv_WriteAsync: previous DMA op is not finished!\n");
		BUG();
	}
	
	g_drv.dma_write_addr = dma_bus_address;
	g_drv.dma_write_size = uLen;

	omap_set_dma_src_params     (g_drv.dma_tx_channel,
									0,			// src_port is only for OMAP1
									OMAP_DMA_AMODE_POST_INC,
									dma_bus_address,
									0, 0);

	omap_set_dma_transfer_params(g_drv.dma_tx_channel, OMAP_DMA_DATA_TYPE_S32, uNumOfElem, uDmaBlockCount, OMAP_DMA_SYNC_FRAME, TIWLAN_MMC_DMA_TX, OMAP_DMA_DST_SYNC);

	omap_start_dma(g_drv.dma_tx_channel);

	/* Continued at sdiodrv_irq() after DMA transfer is finished */
	return 0;
err:
	return -1;
}

/*--------------------------------------------------------------------------------------*/

int sdioDrv_ReadSyncBytes (unsigned int  uFunc, 
                           unsigned int  uHwAddr, 
                           unsigned char *pData, 
                           unsigned int  uLen, 
                           unsigned int  bMore)
{
	unsigned int uCmdArg;
	unsigned int i;
	int          iStatus;

	for (i = 0; i < uLen; i++) {
		uCmdArg = SDIO_CMD52_READ(0, uFunc, 0, uHwAddr);

		iStatus = sdiodrv_send_command(OMAP_HSMMC_CMD52_READ, uCmdArg);

		if (!(iStatus & CC)) {
			PERR("sdioDrv_ReadSyncBytes() SDIO Command error status = 0x%x\n", iStatus);
			return -1;
		}
		else {
			*pData = (unsigned char)(OMAP_HSMMC_READ(RSP10));
		}
#ifdef TI_SDIO_DEBUG
		printk(KERN_INFO "R52: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)*pData);
#endif
		uHwAddr++;
		pData++;
	}

	return 0;
}

/*--------------------------------------------------------------------------------------*/

int sdioDrv_WriteSyncBytes (unsigned int  uFunc, 
                            unsigned int  uHwAddr, 
                            unsigned char *pData, 
                            unsigned int  uLen, 
                            unsigned int  bMore)
{
	unsigned int uCmdArg;
	unsigned int i;
	int          iStatus;

	for (i = 0; i < uLen; i++) {
#ifdef TI_SDIO_DEBUG
		printk(KERN_INFO "W52: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)*pData);
#endif
		uCmdArg = SDIO_CMD52_WRITE(1, uFunc, 0, uHwAddr, *pData);

		iStatus = sdiodrv_send_command(OMAP_HSMMC_CMD52_WRITE, uCmdArg);
		if (!(iStatus & CC)) {
			PERR("sdioDrv_WriteSyncBytes() SDIO Command error status = 0x%x\n", iStatus);
			return -1;
		}
		uHwAddr++;
		pData++;
	}

	return 0;
}

static int sdioDrv_probe(struct platform_device *pdev)
{
	int rc;
	u32 status;
#ifdef SDIO_1_BIT /* see also in SdioAdapter.c */
	unsigned long clock_rate = 6000000;
#else
	unsigned long clock_rate = 24000000;
#endif

	printk(KERN_INFO "TIWLAN SDIO probe: initializing mmc%d device\n", pdev->id + 1);

	/* remember device struct for future DMA operations */
	g_drv.dev = &pdev->dev;
	g_drv.irq = platform_get_irq(pdev, 0);
	if (g_drv.irq < 0)
		return -ENXIO;

        rc= request_irq(OMAP_MMC_IRQ, sdiodrv_irq, 0, SDIO_DRIVER_NAME, &g_drv);
        if (rc != 0) {
                PERR("sdioDrv_InitHw() - request_irq FAILED!!\n");
                return rc;
        }
        sdiodrv_irq_requested = 1;

	spin_lock_init(&g_drv.clk_lock);

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
	dummy_pdev.id = TIWLAN_MMC_CONTROLLER;
	dev_set_name(&dummy_pdev.dev, "mmci-omap-hs.%lu", TIWLAN_MMC_CONTROLLER);
	g_drv.fclk = clk_get(&dummy_pdev.dev, "fck");
#else
	g_drv.fclk = clk_get(&pdev->dev, "mmchs_fck");
#endif
	if (IS_ERR(g_drv.fclk)) {
		rc = PTR_ERR(g_drv.fclk);
		PERR("clk_get(fclk) FAILED !!!\n");
		goto err;
	}
	sdiodrv_fclk_got = 1;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
	g_drv.iclk = clk_get(&dummy_pdev.dev, "ick");
#else
	g_drv.iclk = clk_get(&pdev->dev, "mmchs_ick");
#endif
	if (IS_ERR(g_drv.iclk)) {
		rc = PTR_ERR(g_drv.iclk);
		PERR("clk_get(iclk) FAILED !!!\n");
		goto err;
	}
	sdiodrv_iclk_got = 1;
	
        rc = sdioDrv_clk_enable();
        if (rc) {
                PERR("sdioDrv_probe : clk_enable FAILED !!!\n");
                goto err;
        }

	OMAP3430_mmc_reset();

	//obc - init sequence p. 3600,3617
	/* 1.8V */
	OMAP_HSMMC_WRITE(CAPA,		OMAP_HSMMC_READ(CAPA) | VS18);
	OMAP_HSMMC_WRITE(HCTL,		OMAP_HSMMC_READ(HCTL) | SDVS18);//SDVS fits p. 3650
	/* clock gating */
	OMAP_HSMMC_WRITE(SYSCONFIG, OMAP_HSMMC_READ(SYSCONFIG) | AUTOIDLE);

	/* bus power */
	OMAP_HSMMC_WRITE(HCTL,		OMAP_HSMMC_READ(HCTL) | SDBP);//SDBP fits p. 3650
	/* interrupts */
	OMAP_HSMMC_WRITE(ISE,		0);
	OMAP_HSMMC_WRITE(IE,		IE_EN_MASK);

	//p. 3601 suggests moving to the end
	OMAP3430_mmc_set_clock(clock_rate, &g_drv);
	printk(KERN_INFO "SDIO clock Configuration is now set to %dMhz\n",(int)clock_rate/1000000);

	/* Bus width */
#ifdef SDIO_1_BIT /* see also in SdioAdapter.c */
	PDEBUG("%s() setting %d data lines\n",__FUNCTION__, 1);
	OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) & (ONE_BIT));
#else
	PDEBUG("%s() setting %d data lines\n",__FUNCTION__, 4);
	OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) | (1 << 1));//DTW 4 bits - p. 3650
#endif
	
	/* send the init sequence. 80 clocks of synchronization in the SDIO */
	//doesn't match p. 3601,3617 - obc
	OMAP_HSMMC_WRITE( CON, OMAP_HSMMC_READ(CON) | INIT_STREAM);
	OMAP_HSMMC_SEND_COMMAND( 0, 0);
	status = sdiodrv_poll_status(OMAP_HSMMC_STAT, CC, MMC_TIMEOUT_MS);
	if (!(status & CC)) {
		PERR("sdioDrv_InitHw() SDIO Command error status = 0x%x\n", status);
		rc = -1;
		goto err;
	}
	OMAP_HSMMC_WRITE(CON, OMAP_HSMMC_READ(CON) & ~INIT_STREAM);

	/* Disabling clocks for now */
	sdioDrv_clk_disable();

	return 0;
err:
	sdiodrv_free_resources();
	return rc;
}

static int sdioDrv_remove(struct platform_device *pdev)
{
	printk(KERN_INFO "sdioDrv_remove: calling sdiodrv_shutdown\n");
	
	sdiodrv_shutdown();
	
	return 0;
}

#ifdef CONFIG_PM
static int sdioDrv_suspend(struct platform_device *pdev, pm_message_t state)
{
	printk(KERN_INFO "TISDIO: sdioDrv is suspending\n");
	return 0;
}

/* Routine to resume the MMC device */
static int sdioDrv_resume(struct platform_device *pdev)
{
	printk(KERN_INFO "TISDIO: sdioDrv is resuming\n");
	return 0;
}
#else
#define sdioDrv_suspend		NULL
#define sdioDrv_resume		NULL
#endif

static struct platform_driver sdioDrv_struct = {
	.probe		= sdioDrv_probe,
	.remove		= sdioDrv_remove,
	.suspend	= sdioDrv_suspend,
	.resume		= sdioDrv_resume,
	.driver		= {
		.name = SDIO_DRIVER_NAME,
	},
};

void sdioDrv_register_pm(int (*wlanDrvIf_Start)(void),
						int (*wlanDrvIf_Stop)(void))
{
	g_drv.wlanDrvIf_pm_resume = wlanDrvIf_Start;
	g_drv.wlanDrvIf_pm_suspend = wlanDrvIf_Stop;
}

int sdioDrv_clk_enable(void)
{
       unsigned long flags;
       int ret = 0;

       spin_lock_irqsave(&g_drv.clk_lock, flags);
       if (g_drv.ifclks_enabled)
               goto done;

       ret = clk_enable(g_drv.iclk);
       if (ret)
              goto clk_en_err1;

       ret = clk_enable(g_drv.fclk);
       if (ret)
               goto clk_en_err2;
       g_drv.ifclks_enabled = 1;

       sdioDrv_hsmmc_restore_ctx();

done:
       spin_unlock_irqrestore(&g_drv.clk_lock, flags);
       return ret;

clk_en_err2:
       clk_disable(g_drv.iclk);
clk_en_err1 :
       spin_unlock_irqrestore(&g_drv.clk_lock, flags);
       return ret;
}

void sdioDrv_clk_disable(void)
{
       unsigned long flags;

       spin_lock_irqsave(&g_drv.clk_lock, flags);
       if (!g_drv.ifclks_enabled)
               goto done;

       sdioDrv_hsmmc_save_ctx();

       clk_disable(g_drv.fclk);
       clk_disable(g_drv.iclk);
       g_drv.ifclks_enabled = 0;
done:
       spin_unlock_irqrestore(&g_drv.clk_lock, flags);
}

#ifdef TI_SDIO_STANDALONE
static int __init sdioDrv_init(void)
#else
int __init sdioDrv_init(int sdcnum)
#endif
{
	memset(&g_drv, 0, sizeof(g_drv));
	memset(&hsmmc_ctx, 0, sizeof(hsmmc_ctx));

	printk(KERN_INFO "TIWLAN SDIO init\n");
#ifndef TI_SDIO_STANDALONE
	sdio_init( sdcnum );
#endif
	g_drv.sdio_wq = create_freezeable_workqueue(SDIOWQ_NAME);
	if (!g_drv.sdio_wq) {
		printk("TISDIO: Fail to create SDIO WQ\n");
		return -EINVAL;
	}
	/* Register the sdio driver */
	return platform_driver_register(&sdioDrv_struct);
}

#ifdef TI_SDIO_STANDALONE
static
#endif
void __exit sdioDrv_exit(void)
{
	/* Unregister sdio driver */
	platform_driver_unregister(&sdioDrv_struct);
	if (g_drv.sdio_wq)
		destroy_workqueue(g_drv.sdio_wq);
}

#ifdef TI_SDIO_STANDALONE
module_init(sdioDrv_init);
module_exit(sdioDrv_exit);
#endif

EXPORT_SYMBOL(sdioDrv_ConnectBus);
EXPORT_SYMBOL(sdioDrv_DisconnectBus);
EXPORT_SYMBOL(sdioDrv_ExecuteCmd);
EXPORT_SYMBOL(sdioDrv_ReadSync);
EXPORT_SYMBOL(sdioDrv_WriteSync);
EXPORT_SYMBOL(sdioDrv_ReadAsync);
EXPORT_SYMBOL(sdioDrv_WriteAsync);
EXPORT_SYMBOL(sdioDrv_ReadSyncBytes);
EXPORT_SYMBOL(sdioDrv_WriteSyncBytes);
EXPORT_SYMBOL(sdioDrv_register_pm);
MODULE_DESCRIPTION("TI WLAN SDIO driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS(SDIO_DRIVER_NAME);
MODULE_AUTHOR("Texas Instruments Inc");
#endif
