Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c
new file mode 100644
index 0000000..53b358b
--- /dev/null
+++ b/arch/arm/mach-msm/sdio_al.c
@@ -0,0 +1,3526 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * SDIO-Abstraction-Layer Module.
+ *
+ * To be used with Qualcomm's SDIO-Client connected to this host.
+ */
+#include <sdio_al_private.h>
+
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/gpio.h>
+#include <linux/dma-mapping.h>
+#include <linux/earlysuspend.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+
+#include <mach/dma.h>
+#include <mach/gpio.h>
+#include <mach/subsystem_notif.h>
+
+#include "../../../drivers/mmc/host/msm_sdcc.h"
+
+/**
+ *  Func#0 has SDIO standard registers
+ *  Func#1 is for Mailbox.
+ *  Functions 2..7 are for channels.
+ *  Currently only functions 2..5 are active due to SDIO-Client
+ *  number of pipes.
+ *
+ */
+#define SDIO_AL_MAX_CHANNELS 6
+
+/** Func 1..5 */
+#define SDIO_AL_MAX_FUNCS    (SDIO_AL_MAX_CHANNELS+1)
+#define SDIO_AL_WAKEUP_FUNC  6
+
+/** Number of SDIO-Client pipes */
+#define SDIO_AL_MAX_PIPES    16
+#define SDIO_AL_ACTIVE_PIPES 8
+
+/** CMD53/CMD54 Block size */
+#define SDIO_AL_BLOCK_SIZE   128
+
+/** Func#1 hardware Mailbox base address	 */
+#define HW_MAILBOX_ADDR			0x1000
+
+/** Func#1 peer sdioc software version.
+ *  The header is duplicated also to the mailbox of the other
+ *  functions. It can be used before other functions are enabled. */
+#define SDIOC_SW_HEADER_ADDR		0x0400
+
+/** Func#2..7 software Mailbox base address at 16K */
+#define SDIOC_SW_MAILBOX_ADDR			0x4000
+
+/** Some Mailbox registers address, written by host for
+ control */
+#define PIPES_THRESHOLD_ADDR		0x01000
+
+#define PIPES_0_7_IRQ_MASK_ADDR 	0x01048
+
+#define PIPES_8_15_IRQ_MASK_ADDR	0x0104C
+
+#define FUNC_1_4_MASK_IRQ_ADDR		0x01040
+#define FUNC_5_7_MASK_IRQ_ADDR		0x01044
+#define FUNC_1_4_USER_IRQ_ADDR		0x01050
+#define FUNC_5_7_USER_IRQ_ADDR		0x01054
+
+#define EOT_PIPES_ENABLE		0x00
+
+/** Maximum read/write data available is SDIO-Client limitation */
+#define MAX_DATA_AVAILABLE   		(16*1024)
+#define INVALID_DATA_AVAILABLE  	(0x8000)
+
+/** SDIO-Client HW threshold to generate interrupt to the
+ *  SDIO-Host on write available bytes.
+ */
+#define DEFAULT_WRITE_THRESHOLD 	(1024)
+
+/** SDIO-Client HW threshold to generate interrupt to the
+ *  SDIO-Host on read available bytes, for streaming (non
+ *  packet) rx data.
+ */
+#define DEFAULT_READ_THRESHOLD  	(1024)
+
+/** SW threshold to trigger reading the mailbox. */
+#define DEFAULT_MIN_WRITE_THRESHOLD 	(1024)
+#define DEFAULT_MIN_WRITE_THRESHOLD_STREAMING	(1600)
+
+#define THRESHOLD_DISABLE_VAL  		(0xFFFFFFFF)
+
+
+/** Mailbox polling time for packet channels */
+#define DEFAULT_POLL_DELAY_MSEC		10
+/** Mailbox polling time for streaming channels */
+#define DEFAULT_POLL_DELAY_NOPACKET_MSEC 30
+
+/** The SDIO-Client prepares N buffers of size X per Tx pipe.
+ *  Even when the transfer fills a partial buffer,
+ *  that buffer becomes unusable for the next transfer. */
+#define DEFAULT_PEER_TX_BUF_SIZE	(128)
+
+#define ROUND_UP(x, n) (((x + n - 1) / n) * n)
+
+/** Func#2..7 FIFOs are r/w via
+ sdio_readsb() & sdio_writesb(),when inc_addr=0 */
+#define PIPE_RX_FIFO_ADDR   0x00
+#define PIPE_TX_FIFO_ADDR   0x00
+
+/** Inactivity time to go to sleep in mseconds */
+#define INACTIVITY_TIME_MSEC 30
+#define INITIAL_INACTIVITY_TIME_MSEC 5000
+
+/** Context validity check */
+#define SDIO_AL_SIGNATURE 0xAABBCCDD
+
+/* Vendor Specific Command */
+#define SD_IO_RW_EXTENDED_QCOM 54
+
+#define TIME_TO_WAIT_US 500
+
+#define SDIO_TEST_POSTFIX "_TEST"
+
+#define DATA_DEBUG(x...) if (sdio_al->debug.debug_data_on) pr_info(x)
+#define LPM_DEBUG(x...) if (sdio_al->debug.debug_lpm_on) pr_info(x)
+
+
+/* The index of the SDIO card used for the sdio_al_dloader */
+#define SDIO_BOOTLOADER_CARD_INDEX 1
+
+
+/* SDIO card state machine */
+enum sdio_al_device_state {
+	CARD_INSERTED,
+	CARD_REMOVED,
+	MODEM_RESTART
+};
+
+struct sdio_al_debug {
+
+	u8 debug_lpm_on;
+	u8 debug_data_on;
+	struct dentry *sdio_al_debug_root;
+	struct dentry *sdio_al_debug_lpm_on;
+	struct dentry *sdio_al_debug_data_on;
+	struct dentry *sdio_al_debug_info;
+};
+
+/* Polling time for the inactivity timer for devices that doesn't have
+ * a streaming channel
+ */
+#define SDIO_AL_POLL_TIME_NO_STREAMING 30
+
+#define CHAN_TO_FUNC(x) ((x) + 2 - 1)
+
+/**
+ *  Mailbox structure.
+ *  The Mailbox is located on the SDIO-Client Function#1.
+ *  The mailbox size is 128 bytes, which is one block.
+ *  The mailbox allows the host ton:
+ *  1. Get the number of available bytes on the pipes.
+ *  2. Enable/Disable SDIO-Client interrupt, related to pipes.
+ *  3. Set the Threshold for generating interrupt.
+ *
+ */
+struct sdio_mailbox {
+	u32 pipe_bytes_threshold[SDIO_AL_MAX_PIPES]; /* Addr 0x1000 */
+
+	/* Mask USER interrupts generated towards host - Addr 0x1040 */
+	u32 mask_irq_func_1:8; /* LSB */
+	u32 mask_irq_func_2:8;
+	u32 mask_irq_func_3:8;
+	u32 mask_irq_func_4:8;
+
+	u32 mask_irq_func_5:8;
+	u32 mask_irq_func_6:8;
+	u32 mask_irq_func_7:8;
+	u32 mask_mutex_irq:8;
+
+	/* Mask PIPE interrupts generated towards host - Addr 0x1048 */
+	u32 mask_eot_pipe_0_7:8;
+	u32 mask_thresh_above_limit_pipe_0_7:8;
+	u32 mask_overflow_pipe_0_7:8;
+	u32 mask_underflow_pipe_0_7:8;
+
+	u32 mask_eot_pipe_8_15:8;
+	u32 mask_thresh_above_limit_pipe_8_15:8;
+	u32 mask_overflow_pipe_8_15:8;
+	u32 mask_underflow_pipe_8_15:8;
+
+	/* Status of User interrupts generated towards host - Addr 0x1050 */
+	u32 user_irq_func_1:8;
+	u32 user_irq_func_2:8;
+	u32 user_irq_func_3:8;
+	u32 user_irq_func_4:8;
+
+	u32 user_irq_func_5:8;
+	u32 user_irq_func_6:8;
+	u32 user_irq_func_7:8;
+	u32 user_mutex_irq:8;
+
+	/* Status of PIPE interrupts generated towards host */
+	/* Note: All sources are cleared once they read. - Addr 0x1058 */
+	u32 eot_pipe_0_7:8;
+	u32 thresh_above_limit_pipe_0_7:8;
+	u32 overflow_pipe_0_7:8;
+	u32 underflow_pipe_0_7:8;
+
+	u32 eot_pipe_8_15:8;
+	u32 thresh_above_limit_pipe_8_15:8;
+	u32 overflow_pipe_8_15:8;
+	u32 underflow_pipe_8_15:8;
+
+	u16 pipe_bytes_avail[SDIO_AL_MAX_PIPES];
+};
+
+/** Track pending Rx Packet size */
+struct rx_packet_size {
+	u32 size; /* in bytes */
+	struct list_head	list;
+};
+
+#define PEER_SDIOC_SW_MAILBOX_SIGNATURE 0xFACECAFE
+#define PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE 0x5D107E57
+#define PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE 0xDEADBEEF
+
+/* Allow support in old sdio version */
+#define PEER_SDIOC_OLD_VERSION_MAJOR	0x0002
+
+#define MAX_NUM_OF_SDIO_DEVICES		2
+
+#define INVALID_SDIO_CHAN		0xFF
+
+/**
+ * Peer SDIO-Client software header.
+ */
+struct peer_sdioc_sw_header {
+	u32 signature;
+	u32 version;
+	u32 max_channels;
+	char channel_names[SDIO_AL_MAX_CHANNELS][PEER_CHANNEL_NAME_SIZE];
+	u32 reserved[23];
+};
+
+struct peer_sdioc_boot_sw_header {
+	u32 signature;
+	u32 version;
+	u32 boot_ch_num;
+	u32 reserved[29]; /* 32 - previous fields */
+};
+
+/**
+ * Peer SDIO-Client software mailbox.
+ */
+struct peer_sdioc_sw_mailbox {
+	struct peer_sdioc_sw_header sw_header;
+	struct peer_sdioc_channel_config ch_config[SDIO_AL_MAX_CHANNELS];
+};
+
+/**
+ *  SDIO Abstraction Layer driver context.
+ *
+ *  @pdata -
+ *  @debug -
+ *  @devices - an array of the the devices claimed by sdio_al
+ *  @unittest_mode - a flag to indicate if sdio_al is in
+ *		   unittest mode
+ *  @bootloader_dev - the device which is used for the
+ *                 bootloader
+ *  @subsys_notif_handle - handle for modem restart
+ *                 notifications
+ *
+ */
+struct sdio_al {
+	struct sdio_al_platform_data *pdata;
+	struct sdio_al_debug debug;
+	struct sdio_al_device *devices[MAX_NUM_OF_SDIO_DEVICES];
+	int unittest_mode;
+	struct sdio_al_device *bootloader_dev;
+	void *subsys_notif_handle;
+	int sdioc_major;
+};
+
+struct sdio_al_work {
+	struct work_struct work;
+	struct sdio_al_device *sdio_al_dev;
+};
+
+
+/**
+ *  SDIO Abstraction Layer device context.
+ *
+ *  @card - card claimed.
+ *
+ *  @mailbox - A shadow of the SDIO-Client mailbox.
+ *
+ *  @channel - Channels context.
+ *
+ *  @workqueue - workqueue to read the mailbox and handle
+ *     pending requests. Reading the mailbox should not happen
+ *     in interrupt context.
+ *
+ *  @work - work to submit to workqueue.
+ *
+ *  @is_ready - driver is ready.
+ *
+ *  @ask_mbox - Flag to request reading the mailbox,
+ *					  for different reasons.
+ *
+ *  @wake_lock - Lock when can't sleep.
+ *
+ *  @lpm_chan - Channel to use for LPM (low power mode)
+ *            communication.
+ *
+ *  @is_ok_to_sleep - Mark if driver is OK with going to sleep
+ * 			(no pending transactions).
+ *
+ *  @inactivity_time - time allowed to be in inactivity before
+ * 		going to sleep
+ *
+ *  @timer - timer to use for polling the mailbox.
+ *
+ *  @poll_delay_msec - timer delay for polling the mailbox.
+ *
+ *  @is_err - error detected.
+ *
+ *  @signature - Context Validity Check.
+ *
+ *  @flashless_boot_on - flag to indicate if sdio_al is in
+ *    flshless boot mode
+ *
+ */
+struct sdio_al_device {
+	struct mmc_card *card;
+	struct sdio_mailbox *mailbox;
+	struct sdio_channel channel[SDIO_AL_MAX_CHANNELS];
+
+	struct peer_sdioc_sw_header *sdioc_sw_header;
+	struct peer_sdioc_boot_sw_header *sdioc_boot_sw_header;
+
+	struct workqueue_struct *workqueue;
+	struct sdio_al_work sdio_al_work;
+	struct sdio_al_work boot_work;
+
+	int is_ready;
+
+	wait_queue_head_t   wait_mbox;
+	int ask_mbox;
+	int bootloader_done;
+
+	struct wake_lock wake_lock;
+	int lpm_chan;
+	int is_ok_to_sleep;
+	unsigned long inactivity_time;
+
+	struct timer_list timer;
+	u32 poll_delay_msec;
+	int is_timer_initialized;
+
+	int is_err;
+
+	u32 signature;
+
+	unsigned int clock;
+
+	unsigned int is_suspended;
+
+	int flashless_boot_on;
+
+	int state;
+	int (*lpm_callback)(void *, int);
+};
+
+/*
+ * On the kernel command line specify
+ * sdio_al.debug_lpm_on=1 to enable the LPM debug messages
+ * By default the LPM debug messages are turned off
+ */
+static int debug_lpm_on;
+module_param(debug_lpm_on, int, 0);
+
+/*
+ * On the kernel command line specify
+ * sdio_al.debug_data_on=1 to enable the DATA debug messages
+ * By default the DATA debug messages are turned off
+ */
+static int debug_data_on;
+module_param(debug_data_on, int, 0);
+
+/** The driver context */
+static struct sdio_al *sdio_al;
+
+/* Static functions declaration */
+static int enable_eot_interrupt(struct sdio_al_device *sdio_al_dev,
+				int pipe_index, int enable);
+static int enable_threshold_interrupt(struct sdio_al_device *sdio_al_dev,
+				      int pipe_index, int enable);
+static void sdio_func_irq(struct sdio_func *func);
+static void sdio_al_timer_handler(unsigned long data);
+static int get_min_poll_time_msec(struct sdio_al_device *sdio_al_dev);
+static u32 check_pending_rx_packet(struct sdio_channel *ch, u32 eot);
+static u32 remove_handled_rx_packet(struct sdio_channel *ch);
+static int set_pipe_threshold(struct sdio_al_device *sdio_al_dev,
+			      int pipe_index, int threshold);
+static int sdio_al_wake_up(struct sdio_al_device *sdio_al_dev,
+			   u32 not_from_int);
+static int sdio_al_client_setup(struct sdio_al_device *sdio_al_dev);
+static int enable_mask_irq(struct sdio_al_device *sdio_al_dev,
+			   int func_num, int enable, u8 bit_offset);
+static int sdio_al_enable_func_retry(struct sdio_func *func, const char *name);
+static void sdio_al_print_info(void);
+
+#define SDIO_AL_ERR(func)					\
+	do {							\
+		printk_once(KERN_ERR MODULE_NAME		\
+			":In Error state, ignore %s\n",		\
+			func);					\
+		sdio_al_print_info();				\
+	} while (0)
+
+#ifdef CONFIG_DEBUG_FS
+static int debug_info_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t debug_info_write(struct file *file,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	sdio_al_print_info();
+	return 1;
+}
+
+const struct file_operations debug_info_ops = {
+	.open = debug_info_open,
+	.write = debug_info_write,
+};
+
+/*
+*
+* Trigger on/off for debug messages
+* for trigger off the data messages debug level use:
+* echo 0 > /sys/kernel/debugfs/sdio_al/debug_data_on
+* for trigger on the data messages debug level use:
+* echo 1 > /sys/kernel/debugfs/sdio_al/debug_data_on
+* for trigger off the lpm messages debug level use:
+* echo 0 > /sys/kernel/debugfs/sdio_al/debug_lpm_on
+* for trigger on the lpm messages debug level use:
+* echo 1 > /sys/kernel/debugfs/sdio_al/debug_lpm_on
+*/
+static int sdio_al_debugfs_init(void)
+{
+	sdio_al->debug.sdio_al_debug_root = debugfs_create_dir("sdio_al", NULL);
+	if (!sdio_al->debug.sdio_al_debug_root)
+		return -ENOENT;
+
+	sdio_al->debug.sdio_al_debug_lpm_on = debugfs_create_u8("debug_lpm_on",
+					S_IRUGO | S_IWUGO,
+					sdio_al->debug.sdio_al_debug_root,
+					&sdio_al->debug.debug_lpm_on);
+
+	sdio_al->debug.sdio_al_debug_data_on = debugfs_create_u8(
+					"debug_data_on",
+					S_IRUGO | S_IWUGO,
+					sdio_al->debug.sdio_al_debug_root,
+					&sdio_al->debug.debug_data_on);
+
+	sdio_al->debug.sdio_al_debug_info = debugfs_create_file(
+					"sdio_debug_info",
+					S_IRUGO | S_IWUGO,
+					sdio_al->debug.sdio_al_debug_root,
+					NULL,
+					&debug_info_ops);
+
+	if ((!sdio_al->debug.sdio_al_debug_data_on) &&
+	    (!sdio_al->debug.sdio_al_debug_lpm_on) &&
+	    (!sdio_al->debug.sdio_al_debug_info)
+	    ) {
+		debugfs_remove(sdio_al->debug.sdio_al_debug_root);
+		sdio_al->debug.sdio_al_debug_root = NULL;
+		return -ENOENT;
+	}
+	return 0;
+}
+
+static void sdio_al_debugfs_cleanup(void)
+{
+       debugfs_remove(sdio_al->debug.sdio_al_debug_lpm_on);
+       debugfs_remove(sdio_al->debug.sdio_al_debug_data_on);
+	debugfs_remove(sdio_al->debug.sdio_al_debug_info);
+       debugfs_remove(sdio_al->debug.sdio_al_debug_root);
+}
+#endif
+
+static int sdio_al_verify_func1(struct sdio_al_device *sdio_al_dev,
+				char const *func)
+{
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": %s: NULL sdio_al_dev\n", func);
+		return -ENODEV;
+	}
+	if (!sdio_al_dev->card) {
+		pr_err(MODULE_NAME ": %s: NULL card\n", func);
+		return -ENODEV;
+	}
+	if (!sdio_al_dev->card->sdio_func[0]) {
+		pr_err(MODULE_NAME ": %s: NULL func1\n", func);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+static int sdio_al_verify_dev(struct sdio_al_device *sdio_al_dev,
+			      char const *func)
+{
+	int ret;
+
+	ret = sdio_al_verify_func1(sdio_al_dev, func);
+	if (ret)
+		return ret;
+
+	if ((sdio_al_dev->state == MODEM_RESTART) ||
+	    (sdio_al_dev->state == CARD_REMOVED)) {
+		pr_err(MODULE_NAME ": %s: device state %d\n", func,
+		       sdio_al_dev->state);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void sdio_al_get_into_err_state(struct sdio_al_device *sdio_al_dev)
+{
+	if ((!sdio_al) || (!sdio_al_dev))
+		return;
+
+	sdio_al_dev->is_err = true;
+	sdio_al->debug.debug_data_on = 0;
+	sdio_al->debug.debug_lpm_on = 0;
+	sdio_al_print_info();
+}
+
+void sdio_al_register_lpm_cb(void *device_handle,
+				       int(*lpm_callback)(void *, int))
+{
+	struct sdio_al_device *sdio_al_dev =
+		(struct sdio_al_device *) device_handle;
+
+	if (!sdio_al_dev) {
+		pr_err(MODULE_NAME ": %s - device_handle is NULL\n",
+			__func__);
+		return;
+	}
+
+	if (lpm_callback) {
+		sdio_al_dev->lpm_callback = lpm_callback;
+		lpm_callback((void *)sdio_al_dev,
+					   sdio_al_dev->is_ok_to_sleep);
+	}
+	LPM_DEBUG(MODULE_NAME ": %s - device %d registered for wakeup "
+	"callback\n", __func__, sdio_al_dev->card->host->index);
+}
+
+void sdio_al_unregister_lpm_cb(void *device_handle)
+{
+	struct sdio_al_device *sdio_al_dev =
+		(struct sdio_al_device *) device_handle;
+
+	if (!sdio_al_dev) {
+		pr_err(MODULE_NAME ": %s - device_handle is NULL\n",
+			__func__);
+		return;
+	}
+
+	sdio_al_dev->lpm_callback = NULL;
+	LPM_DEBUG(MODULE_NAME ": %s - device %d unregister for wakeup "
+	"callback\n", __func__, sdio_al_dev->card->host->index);
+}
+
+static void sdio_al_vote_for_sleep(struct sdio_al_device *sdio_al_dev,
+				   int is_vote_for_sleep)
+{
+	pr_debug(MODULE_NAME ": %s()", __func__);
+
+	if (is_vote_for_sleep) {
+		LPM_DEBUG(MODULE_NAME ": %s - sdio vote for Sleep", __func__);
+		wake_unlock(&sdio_al_dev->wake_lock);
+	} else {
+		LPM_DEBUG(MODULE_NAME ": %s - sdio vote against sleep",
+			  __func__);
+		wake_lock(&sdio_al_dev->wake_lock);
+	}
+
+	if (sdio_al_dev->lpm_callback != NULL) {
+		LPM_DEBUG(MODULE_NAME ": %s - is_vote_for_sleep=%d for "
+			"card#%d, calling callback...",
+			__func__,
+			is_vote_for_sleep,
+			sdio_al_dev->card->host->index);
+		sdio_al_dev->lpm_callback((void *)sdio_al_dev,
+					   is_vote_for_sleep);
+	}
+}
+
+/**
+ *  Write SDIO-Client lpm information
+ *  Should only be called with host claimed.
+ */
+static int write_lpm_info(struct sdio_al_device *sdio_al_dev)
+{
+	struct sdio_func *lpm_func =
+		sdio_al_dev->card->sdio_func[sdio_al_dev->lpm_chan+1];
+	int offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config)+
+		sizeof(struct peer_sdioc_channel_config) *
+		sdio_al_dev->lpm_chan+
+		offsetof(struct peer_sdioc_channel_config, is_host_ok_to_sleep);
+	int ret;
+
+	if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) {
+		pr_err(MODULE_NAME ":Invalid lpm_chan for card %d\n",
+				   sdio_al_dev->card->host->index);
+		return -EINVAL;
+	}
+
+	pr_debug(MODULE_NAME ":write_lpm_info is_ok_to_sleep=%d, device %d\n",
+		 sdio_al_dev->is_ok_to_sleep,
+		 sdio_al_dev->card->host->index);
+
+	ret = sdio_memcpy_toio(lpm_func, SDIOC_SW_MAILBOX_ADDR+offset,
+				&sdio_al_dev->is_ok_to_sleep, sizeof(u32));
+	if (ret) {
+		pr_err(MODULE_NAME ":failed to write lpm info for card %d\n",
+				   sdio_al_dev->card->host->index);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Set inactivity counter to intial value to allow clients come up */
+static inline void start_inactive_time(struct sdio_al_device *sdio_al_dev)
+{
+	sdio_al_dev->inactivity_time = jiffies +
+		msecs_to_jiffies(INITIAL_INACTIVITY_TIME_MSEC);
+}
+
+static inline void restart_inactive_time(struct sdio_al_device *sdio_al_dev)
+{
+	sdio_al_dev->inactivity_time = jiffies +
+		msecs_to_jiffies(INACTIVITY_TIME_MSEC);
+}
+
+static inline int is_inactive_time_expired(struct sdio_al_device *sdio_al_dev)
+{
+	return time_after(jiffies, sdio_al_dev->inactivity_time);
+}
+
+
+static int is_user_irq_enabled(struct sdio_al_device *sdio_al_dev,
+			       int func_num)
+{
+	int ret = 0;
+	struct sdio_func *func1;
+	u32 user_irq = 0;
+	u32 addr = 0;
+	u32 offset = 0;
+	u32 masked_user_irq = 0;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return 0;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	if (func_num < 4) {
+		addr = FUNC_1_4_USER_IRQ_ADDR;
+		offset = func_num * 8;
+	} else {
+		addr = FUNC_5_7_USER_IRQ_ADDR;
+		offset = (func_num - 4) * 8;
+	}
+
+	user_irq = sdio_readl(func1, addr, &ret);
+	if (ret) {
+		pr_debug(MODULE_NAME ":read_user_irq fail\n");
+		return 0;
+	}
+
+	masked_user_irq = (user_irq >> offset) && 0xFF;
+	if (masked_user_irq == 0x1) {
+		pr_info(MODULE_NAME ":user_irq enabled\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+static void sdio_al_sleep(struct sdio_al_device *sdio_al_dev,
+			  struct mmc_host *host)
+{
+	int i;
+
+	/* Go to sleep */
+	LPM_DEBUG(MODULE_NAME  ":Inactivity timer expired."
+		" Going to sleep\n");
+	/* Stop mailbox timer */
+	sdio_al_dev->poll_delay_msec = 0;
+	del_timer_sync(&sdio_al_dev->timer);
+	/* Make sure we get interrupt for non-packet-mode right away */
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		struct sdio_channel *ch = &sdio_al_dev->channel[i];
+		if ((!ch->is_valid) || (!ch->is_open))
+			continue;
+		if (ch->is_packet_mode == false) {
+			ch->read_threshold = 1;
+			set_pipe_threshold(sdio_al_dev,
+					   ch->rx_pipe_index,
+					   ch->read_threshold);
+		}
+	}
+	/* Mark HOST_OK_TOSLEEP */
+	sdio_al_dev->is_ok_to_sleep = 1;
+	write_lpm_info(sdio_al_dev);
+
+	/* Clock rate is required to enable the clock and set its rate.
+	 * Hence, save the clock rate before disabling it */
+	sdio_al_dev->clock = host->ios.clock;
+	/* Disable clocks here */
+	host->ios.clock = 0;
+	msmsdcc_lpm_enable(host);
+	LPM_DEBUG(MODULE_NAME ":Finished sleep sequence for card %d. "
+			    "Sleep now.\n",
+		sdio_al_dev->card->host->index);
+	/* Release wakelock */
+	sdio_al_vote_for_sleep(sdio_al_dev, 1);
+}
+
+
+/**
+ *  Read SDIO-Client Mailbox from Function#1.thresh_pipe
+ *
+ *  The mailbox contain the bytes available per pipe,
+ *  and the End-Of-Transfer indication per pipe (if available).
+ *
+ * WARNING: Each time the Mailbox is read from the client, the
+ * read_bytes_avail is incremented with another pending
+ * transfer. Therefore, a pending rx-packet should be added to a
+ * list before the next read of the mailbox.
+ *
+ * This function should run from a workqueue context since it
+ * notifies the clients.
+ *
+ * This function assumes that sdio_claim_host was called before
+ * calling it.
+ *
+ */
+static int read_mailbox(struct sdio_al_device *sdio_al_dev, int from_isr)
+{
+	int ret;
+	struct sdio_func *func1 = sdio_al_dev->card->sdio_func[0];
+	struct sdio_mailbox *mailbox = sdio_al_dev->mailbox;
+	struct mmc_host *host = func1->card->host;
+	u32 new_write_avail = 0;
+	u32 old_write_avail = 0;
+	u32 any_read_avail = 0;
+	u32 any_write_pending = 0;
+	int i;
+	u32 rx_notify_bitmask = 0;
+	u32 tx_notify_bitmask = 0;
+	u32 eot_pipe = 0;
+	u32 thresh_pipe = 0;
+	u32 overflow_pipe = 0;
+	u32 underflow_pipe = 0;
+	u32 thresh_intr_mask = 0;
+
+	if (sdio_al_dev->is_err) {
+		SDIO_AL_ERR(__func__);
+		return 0;
+	}
+
+	pr_debug(MODULE_NAME ":start %s from_isr = %d for card %d.\n"
+		 , __func__, from_isr, sdio_al_dev->card->host->index);
+
+	pr_debug(MODULE_NAME ":before sdio_memcpy_fromio.\n");
+	ret = sdio_memcpy_fromio(func1, mailbox,
+			HW_MAILBOX_ADDR, sizeof(*mailbox));
+	pr_debug(MODULE_NAME ":after sdio_memcpy_fromio.\n");
+
+	eot_pipe =	(mailbox->eot_pipe_0_7) |
+			(mailbox->eot_pipe_8_15<<8);
+	thresh_pipe = 	(mailbox->thresh_above_limit_pipe_0_7) |
+			(mailbox->thresh_above_limit_pipe_8_15<<8);
+
+	overflow_pipe = (mailbox->overflow_pipe_0_7) |
+			(mailbox->overflow_pipe_8_15<<8);
+	underflow_pipe = mailbox->underflow_pipe_0_7 |
+			(mailbox->underflow_pipe_8_15<<8);
+	thresh_intr_mask =
+		(mailbox->mask_thresh_above_limit_pipe_0_7) |
+		(mailbox->mask_thresh_above_limit_pipe_8_15<<8);
+
+	if (ret) {
+		pr_err(MODULE_NAME ":Fail to read Mailbox for card %d,"
+				    " goto error state\n",
+		       sdio_al_dev->card->host->index);
+		sdio_al_get_into_err_state(sdio_al_dev);
+		/* Stop the timer to stop reading the mailbox */
+		sdio_al_dev->poll_delay_msec = 0;
+		goto exit_err;
+	}
+
+	if (overflow_pipe || underflow_pipe)
+		pr_err(MODULE_NAME ":Mailbox ERROR "
+				"overflow=0x%x, underflow=0x%x\n",
+				overflow_pipe, underflow_pipe);
+
+	/* In case of modem reset we would like to read the daya from the modem
+	   to clear the interrupts but do not process it */
+	if (sdio_al_dev->state != CARD_INSERTED) {
+		pr_err(MODULE_NAME ":sdio_al_device (card %d) is in invalid "
+				   "state %d\n",
+				sdio_al_dev->card->host->index,
+				sdio_al_dev->state);
+		return -ENODEV;
+	}
+
+	pr_debug(MODULE_NAME ":card %d: eot=0x%x, thresh=0x%x\n",
+			sdio_al_dev->card->host->index,
+			eot_pipe, thresh_pipe);
+
+	/* Scan for Rx Packets available and update read available bytes */
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		struct sdio_channel *ch = &sdio_al_dev->channel[i];
+		u32 old_read_avail;
+		u32 read_avail;
+		u32 new_packet_size = 0;
+
+		if ((!ch->is_valid) || (!ch->is_open))
+			continue;
+
+		old_read_avail = ch->read_avail;
+		read_avail = mailbox->pipe_bytes_avail[ch->rx_pipe_index];
+
+		if (read_avail > INVALID_DATA_AVAILABLE) {
+			pr_err(MODULE_NAME
+				 ":Invalid read_avail 0x%x for pipe %d\n",
+				 read_avail, ch->rx_pipe_index);
+			continue;
+		}
+		any_read_avail |= read_avail | old_read_avail;
+		ch->statistics.last_any_read_avail = any_read_avail;
+		ch->statistics.last_read_avail = read_avail;
+		ch->statistics.last_old_read_avail = old_read_avail;
+
+		if (ch->is_packet_mode)
+			new_packet_size = check_pending_rx_packet(ch, eot_pipe);
+		else
+			ch->read_avail = read_avail;
+
+		if ((ch->is_packet_mode) && (new_packet_size > 0)) {
+			rx_notify_bitmask |= (1<<ch->num);
+			ch->statistics.total_notifs++;
+		}
+
+		if ((!ch->is_packet_mode) && (ch->read_avail > 0) &&
+		    (old_read_avail == 0)) {
+			rx_notify_bitmask |= (1<<ch->num);
+			ch->statistics.total_notifs++;
+		}
+	}
+
+	/* Update Write available */
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		struct sdio_channel *ch = &sdio_al_dev->channel[i];
+
+		if ((!ch->is_valid) || (!ch->is_open))
+			continue;
+
+		new_write_avail = mailbox->pipe_bytes_avail[ch->tx_pipe_index];
+
+		if (new_write_avail > INVALID_DATA_AVAILABLE) {
+			pr_err(MODULE_NAME
+				 ":Invalid write_avail 0x%x for pipe %d\n",
+				 new_write_avail, ch->tx_pipe_index);
+			continue;
+		}
+
+		old_write_avail = ch->write_avail;
+		ch->write_avail = new_write_avail;
+
+		if ((old_write_avail <= ch->min_write_avail) &&
+			(new_write_avail >= ch->min_write_avail))
+			tx_notify_bitmask |= (1<<ch->num);
+
+		/* There is not enough write avail for this channel.
+		   We need to keep reading mailbox to wait for the appropriate
+		   write avail and cannot sleep. Ignore SMEM channel that has
+		   only one direction. */
+		if (strcmp(ch->name, "SDIO_SMEM"))
+			any_write_pending |=
+			(new_write_avail < ch->ch_config.max_tx_threshold);
+	}
+
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		struct sdio_channel *ch = &sdio_al_dev->channel[i];
+
+		if ((!ch->is_valid) || (!ch->is_open) || (ch->notify == NULL))
+			continue;
+
+		if (rx_notify_bitmask & (1<<ch->num))
+			ch->notify(ch->priv,
+					   SDIO_EVENT_DATA_READ_AVAIL);
+
+		if (tx_notify_bitmask & (1<<ch->num))
+			ch->notify(ch->priv,
+					   SDIO_EVENT_DATA_WRITE_AVAIL);
+	}
+
+
+	if ((rx_notify_bitmask == 0) && (tx_notify_bitmask == 0) &&
+	    !any_read_avail && !any_write_pending) {
+		DATA_DEBUG(MODULE_NAME ":Nothing to Notify for card %d\n",
+			   sdio_al_dev->card->host->index);
+		if (is_inactive_time_expired(sdio_al_dev))
+			sdio_al_sleep(sdio_al_dev, host);
+	} else {
+		DATA_DEBUG(MODULE_NAME ":Notify bitmask for card %d "
+				       "rx=0x%x, tx=0x%x.\n",
+			sdio_al_dev->card->host->index, rx_notify_bitmask,
+			   tx_notify_bitmask);
+		/* Restart inactivity timer if any activity on the channel */
+		restart_inactive_time(sdio_al_dev);
+	}
+
+	pr_debug(MODULE_NAME ":end %s.\n", __func__);
+
+exit_err:
+	return ret;
+}
+
+/**
+ *  Check pending rx packet when reading the mailbox.
+ */
+static u32 check_pending_rx_packet(struct sdio_channel *ch, u32 eot)
+{
+	u32 rx_pending;
+	u32 rx_avail;
+	u32 new_packet_size = 0;
+	struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev;
+
+
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": NULL sdio_al_dev for channel %s\n",
+				 ch->name);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ch->ch_lock);
+
+	rx_pending = ch->rx_pending_bytes;
+	rx_avail = sdio_al_dev->mailbox->pipe_bytes_avail[ch->rx_pipe_index];
+
+	pr_debug(MODULE_NAME ":pipe %d of card %d rx_avail=0x%x, "
+			     "rx_pending=0x%x\n",
+	   ch->rx_pipe_index, sdio_al_dev->card->host->index, rx_avail,
+		 rx_pending);
+
+
+	/* new packet detected */
+	if (eot & (1<<ch->rx_pipe_index)) {
+		struct rx_packet_size *p = NULL;
+		new_packet_size = rx_avail - rx_pending;
+
+		if ((rx_avail <= rx_pending)) {
+			pr_err(MODULE_NAME ":Invalid new packet size."
+					    " rx_avail=%d.\n", rx_avail);
+			new_packet_size = 0;
+			goto exit_err;
+		}
+
+		p = kzalloc(sizeof(*p), GFP_KERNEL);
+		if (p == NULL)
+			goto exit_err;
+		p->size = new_packet_size;
+		/* Add new packet as last */
+		list_add_tail(&p->list, &ch->rx_size_list_head);
+		ch->rx_pending_bytes += new_packet_size;
+
+		if (ch->read_avail == 0)
+			ch->read_avail = new_packet_size;
+	}
+
+exit_err:
+	mutex_unlock(&ch->ch_lock);
+
+	return new_packet_size;
+}
+
+
+
+/**
+ *  Remove first pending packet from the list.
+ */
+static u32 remove_handled_rx_packet(struct sdio_channel *ch)
+{
+	struct rx_packet_size *p = NULL;
+
+	mutex_lock(&ch->ch_lock);
+
+	ch->rx_pending_bytes -= ch->read_avail;
+
+	if (!list_empty(&ch->rx_size_list_head)) {
+		p = list_first_entry(&ch->rx_size_list_head,
+			struct rx_packet_size, list);
+		list_del(&p->list);
+		kfree(p);
+	}
+
+	if (list_empty(&ch->rx_size_list_head))	{
+		ch->read_avail = 0;
+	} else {
+		p = list_first_entry(&ch->rx_size_list_head,
+			struct rx_packet_size, list);
+		ch->read_avail = p->size;
+	}
+
+	mutex_unlock(&ch->ch_lock);
+
+	return ch->read_avail;
+}
+
+
+/**
+ *  Bootloader worker function.
+ *
+ *  @note: clear the bootloader_done flag only after reading the
+ *  mailbox, to ignore more requests while reading the mailbox.
+ */
+static void boot_worker(struct work_struct *work)
+{
+	int ret = 0;
+	int func_num = 0;
+	int i;
+	struct sdio_al_device *sdio_al_dev = NULL;
+	struct sdio_al_work *sdio_al_work = container_of(work,
+							 struct sdio_al_work,
+							 work);
+
+	if (sdio_al_work == NULL) {
+		pr_err(MODULE_NAME ": %s: NULL sdio_al_work\n", __func__);
+		return;
+	}
+
+	sdio_al_dev = sdio_al_work->sdio_al_dev;
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": %s: NULL sdio_al_dev\n", __func__);
+		return;
+	}
+	pr_info(MODULE_NAME ":Bootloader Worker Started, "
+			    "wait for bootloader_done event..\n");
+	wait_event(sdio_al_dev->wait_mbox,
+		   sdio_al_dev->bootloader_done);
+	pr_info(MODULE_NAME ":Got bootloader_done event..\n");
+	/* Do polling until MDM is up */
+	for (i = 0; i < 5000; ++i) {
+		if (sdio_al_verify_dev(sdio_al_dev, __func__))
+			return;
+		sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+		if (is_user_irq_enabled(sdio_al_dev, func_num)) {
+			sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+			sdio_al_dev->bootloader_done = 0;
+			ret = sdio_al_client_setup(sdio_al_dev);
+			if (ret) {
+				pr_err(MODULE_NAME ":"
+				"sdio_al_client_setup failed, "
+				"for card %d ret=%d\n",
+				sdio_al_dev->card->host->index,
+				ret);
+				sdio_al_get_into_err_state(sdio_al_dev);
+			}
+			goto done;
+		}
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		msleep(100);
+	}
+	pr_err(MODULE_NAME ":Timeout waiting for user_irq for card %d\n",
+	       sdio_al_dev->card->host->index);
+	sdio_al_get_into_err_state(sdio_al_dev);
+
+done:
+	pr_debug(MODULE_NAME ":Boot Worker for card %d Exit!\n",
+		sdio_al_dev->card->host->index);
+}
+
+/**
+ *  Worker function.
+ *
+ *  @note: clear the ask_mbox flag only after
+ *  	 reading the mailbox, to ignore more requests while
+ *  	 reading the mailbox.
+ */
+static void worker(struct work_struct *work)
+{
+	int ret = 0;
+	struct sdio_al_device *sdio_al_dev = NULL;
+	struct sdio_al_work *sdio_al_work = container_of(work,
+							 struct sdio_al_work,
+							 work);
+	if (sdio_al_work == NULL) {
+		pr_err(MODULE_NAME ": worker: NULL sdio_al_work\n");
+		return;
+	}
+
+	sdio_al_dev = sdio_al_work->sdio_al_dev;
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": worker: NULL sdio_al_dev\n");
+		return;
+	}
+	pr_debug(MODULE_NAME ":Worker Started..\n");
+	while ((sdio_al_dev->is_ready) && (ret == 0)) {
+		pr_debug(MODULE_NAME ":Wait for read mailbox request..\n");
+		wait_event(sdio_al_dev->wait_mbox, sdio_al_dev->ask_mbox);
+		if (sdio_al_verify_dev(sdio_al_dev, __func__))
+			break;
+		if (!sdio_al_dev->is_ready)
+			break;
+		sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+		if (sdio_al_dev->is_ok_to_sleep) {
+			ret = sdio_al_wake_up(sdio_al_dev, 1);
+			if (ret) {
+				sdio_release_host(
+					sdio_al_dev->card->sdio_func[0]);
+				return;
+			}
+		}
+		ret = read_mailbox(sdio_al_dev, false);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		sdio_al_dev->ask_mbox = false;
+	}
+	pr_debug(MODULE_NAME ":Worker Exit!\n");
+}
+
+/**
+ *  Write command using CMD54 rather than CMD53.
+ *  Writing with CMD54 generate EOT interrupt at the
+ *  SDIO-Client.
+ *  Based on mmc_io_rw_extended()
+ */
+static int sdio_write_cmd54(struct mmc_card *card, unsigned fn,
+	unsigned addr, const u8 *buf,
+	unsigned blocks, unsigned blksz)
+{
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_data data;
+	struct scatterlist sg;
+	int incr_addr = 1; /* MUST */
+	int write = 1;
+
+	BUG_ON(!card);
+	BUG_ON(fn > 7);
+	BUG_ON(blocks == 1 && blksz > 512);
+	WARN_ON(blocks == 0);
+	WARN_ON(blksz == 0);
+
+	write = true;
+	pr_debug(MODULE_NAME ":sdio_write_cmd54()"
+		"fn=%d,buf=0x%x,blocks=%d,blksz=%d\n",
+		fn, (u32) buf, blocks, blksz);
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+
+	cmd.opcode = SD_IO_RW_EXTENDED_QCOM;
+
+	cmd.arg = write ? 0x80000000 : 0x00000000;
+	cmd.arg |= fn << 28;
+	cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
+	cmd.arg |= addr << 9;
+	if (blocks == 1 && blksz <= 512)
+		cmd.arg |= (blksz == 512) ? 0 : blksz;  /* byte mode */
+	else
+		cmd.arg |= 0x08000000 | blocks; 	/* block mode */
+	cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+	data.blksz = blksz;
+	data.blocks = blocks;
+	data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+	data.sg = &sg;
+	data.sg_len = 1;
+
+	sg_init_one(&sg, buf, blksz * blocks);
+
+	mmc_set_data_timeout(&data, card);
+
+	mmc_wait_for_req(card->host, &mrq);
+
+	if (cmd.error)
+		return cmd.error;
+	if (data.error)
+		return data.error;
+
+	if (mmc_host_is_spi(card->host)) {
+		/* host driver already reported errors */
+	} else {
+		if (cmd.resp[0] & R5_ERROR) {
+			pr_err(MODULE_NAME ":%s: R5_ERROR", __func__);
+			return -EIO;
+		}
+		if (cmd.resp[0] & R5_FUNCTION_NUMBER) {
+			pr_err(MODULE_NAME ":%s: R5_FUNCTION_NUMBER", __func__);
+			return -EINVAL;
+		}
+		if (cmd.resp[0] & R5_OUT_OF_RANGE) {
+			pr_err(MODULE_NAME ":%s: R5_OUT_OF_RANGE", __func__);
+			return -ERANGE;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ *  Write data to channel.
+ *  Handle different data size types.
+ *
+ */
+static int sdio_ch_write(struct sdio_channel *ch, const u8 *buf, u32 len)
+{
+	int ret = 0;
+	unsigned blksz = ch->func->cur_blksize;
+	int blocks = len / blksz;
+	int remain_bytes = len % blksz;
+	struct mmc_card *card = NULL;
+	u32 fn = ch->func->num;
+
+	if (len == 0) {
+		pr_err(MODULE_NAME ":channel %s trying to write 0 bytes\n",
+			ch->name);
+		return -EINVAL;
+	}
+
+	card = ch->func->card;
+
+	if (remain_bytes) {
+		/* CMD53 */
+		if (blocks) {
+			ret = sdio_memcpy_toio(ch->func, PIPE_TX_FIFO_ADDR,
+					       (void *) buf, blocks*blksz);
+			if (ret != 0) {
+				pr_err(MODULE_NAME ":%s: sdio_memcpy_toio "
+						   "failed for channel %s\n",
+							__func__, ch->name);
+				ch->sdio_al_dev->is_err = true;
+				return ret;
+			}
+		}
+
+		buf += (blocks*blksz);
+
+		ret = sdio_write_cmd54(card, fn, PIPE_TX_FIFO_ADDR,
+				buf, 1, remain_bytes);
+	} else {
+		ret = sdio_write_cmd54(card, fn, PIPE_TX_FIFO_ADDR,
+				buf, blocks, blksz);
+	}
+
+	if (ret != 0) {
+		pr_err(MODULE_NAME ":%s: sdio_write_cmd54 "
+				   "failed for channel %s\n",
+					__func__, ch->name);
+		ch->sdio_al_dev->is_err = true;
+		return ret;
+	}
+
+	return ret;
+}
+
+static int sdio_al_bootloader_completed(void)
+{
+	int i;
+
+	pr_debug(MODULE_NAME ":sdio_al_bootloader_completed was called\n");
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) {
+		struct sdio_al_device *dev = NULL;
+		if (sdio_al->devices[i] == NULL)
+			continue;
+		dev = sdio_al->devices[i];
+		dev->bootloader_done = 1;
+		wake_up(&dev->wait_mbox);
+	}
+
+	return 0;
+}
+
+static int sdio_al_wait_for_bootloader_comp(struct sdio_al_device *sdio_al_dev)
+{
+	int ret = 0;
+
+	struct sdio_func *func1;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	sdio_claim_host(func1);
+	/*
+	 * Enable function 0 interrupt mask to allow 9k to raise this interrupt
+	 * in power-up. When sdio_downloader will notify its completion
+	 * we will poll on this interrupt to wait for 9k power-up
+	 */
+	ret = enable_mask_irq(sdio_al_dev, 0, 1, 0);
+	if (ret) {
+		pr_err(MODULE_NAME ":Enable_mask_irq for card %d failed, "
+				   "ret=%d\n",
+		       sdio_al_dev->card->host->index, ret);
+		sdio_release_host(func1);
+		return ret;
+	}
+
+	sdio_release_host(func1);
+
+	/*
+	 * Start bootloader worker that will wait for the bootloader
+	 * completion
+	 */
+	sdio_al_dev->boot_work.sdio_al_dev = sdio_al_dev;
+	INIT_WORK(&sdio_al_dev->boot_work.work, boot_worker);
+	sdio_al_dev->bootloader_done = 0;
+	queue_work(sdio_al_dev->workqueue, &sdio_al_dev->boot_work.work);
+
+	return 0;
+}
+
+static int sdio_al_bootloader_setup(void)
+{
+	int ret = 0;
+	struct sdio_func *func1;
+	struct sdio_al_device *bootloader_dev = sdio_al->bootloader_dev;
+
+	if (bootloader_dev == NULL) {
+		pr_err(MODULE_NAME ":No bootloader_dev\n");
+		return -ENODEV;
+	}
+
+
+	if (bootloader_dev->flashless_boot_on) {
+		pr_info(MODULE_NAME ":Already in boot process.\n");
+		return 0;
+	}
+
+	func1 = bootloader_dev->card->sdio_func[0];
+	if (!func1) {
+		pr_err(MODULE_NAME ": %s: NULL func1\n", __func__);
+		return -ENODEV;
+	}
+
+	bootloader_dev->sdioc_boot_sw_header
+		= kzalloc(sizeof(*bootloader_dev->sdioc_boot_sw_header),
+			  GFP_KERNEL);
+	if (bootloader_dev->sdioc_boot_sw_header == NULL) {
+		pr_err(MODULE_NAME ":fail to allocate sdioc boot sw header.\n");
+		return -ENOMEM;
+	}
+
+	sdio_claim_host(func1);
+
+	ret = sdio_memcpy_fromio(func1,
+				 bootloader_dev->sdioc_boot_sw_header,
+				 SDIOC_SW_HEADER_ADDR,
+				 sizeof(struct peer_sdioc_boot_sw_header));
+	if (ret) {
+		pr_err(MODULE_NAME ":fail to read sdioc boot sw header.\n");
+		sdio_release_host(func1);
+		goto exit_err;
+	}
+
+	if (bootloader_dev->sdioc_boot_sw_header->signature !=
+	    (u32) PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE) {
+		pr_err(MODULE_NAME ":invalid mailbox signature 0x%x.\n",
+		       bootloader_dev->sdioc_boot_sw_header->signature);
+		sdio_release_host(func1);
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	/* Upper byte has to be equal - no backward compatibility for unequal */
+	if ((bootloader_dev->sdioc_boot_sw_header->version >> 16) !=
+	    (sdio_al->pdata->peer_sdioc_boot_version_major)) {
+		pr_err(MODULE_NAME ": HOST(0x%x) and CLIENT(0x%x) SDIO_AL BOOT "
+		       "VERSION don't match\n",
+		       ((sdio_al->pdata->peer_sdioc_boot_version_major<<16)+
+			sdio_al->pdata->peer_sdioc_boot_version_minor),
+		       bootloader_dev->sdioc_boot_sw_header->version);
+		sdio_release_host(func1);
+		ret = -EIO;
+		goto exit_err;
+	}
+
+	pr_info(MODULE_NAME ": SDIOC BOOT SW version 0x%x\n",
+		bootloader_dev->sdioc_boot_sw_header->version);
+
+	bootloader_dev->flashless_boot_on = true;
+
+	sdio_release_host(func1);
+
+	ret = sdio_al_wait_for_bootloader_comp(bootloader_dev);
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_al_wait_for_bootloader_comp failed, "
+				   "err=%d\n", ret);
+		goto exit_err;
+	}
+
+	ret = sdio_downloader_setup(bootloader_dev->card, 1,
+			bootloader_dev->sdioc_boot_sw_header->boot_ch_num,
+			sdio_al_bootloader_completed);
+
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_downloader_setup failed, err=%d\n",
+			ret);
+		goto exit_err;
+	}
+
+	pr_info(MODULE_NAME ":In Flashless boot, waiting for its "
+		"completion\n");
+
+
+exit_err:
+	pr_info(MODULE_NAME ":free sdioc_boot_sw_header.\n");
+	kfree(bootloader_dev->sdioc_boot_sw_header);
+	bootloader_dev->sdioc_boot_sw_header = NULL;
+	bootloader_dev = NULL;
+
+	return ret;
+}
+
+
+/**
+ *  Read SDIO-Client software header
+ *
+ */
+static int read_sdioc_software_header(struct sdio_al_device *sdio_al_dev,
+				      struct peer_sdioc_sw_header *header)
+{
+	int ret;
+	int i;
+	int test_version = 0;
+	int sdioc_test_version = 0;
+
+	pr_debug(MODULE_NAME ":reading sdioc sw header.\n");
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+
+	ret = sdio_memcpy_fromio(sdio_al_dev->card->sdio_func[0], header,
+			SDIOC_SW_HEADER_ADDR, sizeof(*header));
+	if (ret) {
+		pr_err(MODULE_NAME ":fail to read sdioc sw header.\n");
+		goto exit_err;
+	}
+
+	if (header->signature == (u32)PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE) {
+		pr_info(MODULE_NAME ":SDIOC SW unittest signature. 0x%x\n",
+			header->signature);
+		sdio_al->unittest_mode = true;
+		/* Verify test code compatibility with the modem */
+		sdioc_test_version = (header->version & 0xFF00) >> 8;
+		test_version = sdio_al->pdata->peer_sdioc_version_minor >> 8;
+		if (test_version != sdioc_test_version)
+			pr_err(MODULE_NAME ":HOST(0x%x) and CLIENT(0x%x) "
+			       "testing VERSION don't match, tests may fail\n",
+			       test_version,
+			       sdioc_test_version);
+	}
+
+	if ((header->signature != (u32) PEER_SDIOC_SW_MAILBOX_SIGNATURE) &&
+	    (header->signature != (u32) PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE)) {
+		pr_err(MODULE_NAME ":SDIOC SW invalid signature. 0x%x\n",
+			header->signature);
+		goto exit_err;
+	}
+	/* Upper byte has to be equal - no backward compatibility for unequal */
+	sdio_al->sdioc_major = header->version >> 16;
+	if (sdio_al->pdata->allow_sdioc_version_major_2) {
+		if ((sdio_al->sdioc_major !=
+		    sdio_al->pdata->peer_sdioc_version_major) &&
+		    (sdio_al->sdioc_major != PEER_SDIOC_OLD_VERSION_MAJOR)) {
+			pr_err(MODULE_NAME ": HOST(0x%x) and CLIENT(0x%x) "
+			       "SDIO_AL VERSION don't match\n",
+			       ((sdio_al->pdata->peer_sdioc_version_major<<16)+
+				sdio_al->pdata->peer_sdioc_version_minor),
+			       header->version);
+			goto exit_err;
+		}
+	} else {
+		if (sdio_al->sdioc_major !=
+		    sdio_al->pdata->peer_sdioc_version_major) {
+			pr_err(MODULE_NAME ": HOST(0x%x) and CLIENT(0x%x) "
+			       "SDIO_AL VERSION don't match\n",
+			       ((sdio_al->pdata->peer_sdioc_version_major<<16)+
+				sdio_al->pdata->peer_sdioc_version_minor),
+			       header->version);
+			goto exit_err;
+		}
+	}
+
+	pr_info(MODULE_NAME ":SDIOC SW version 0x%x\n", header->version);
+
+	sdio_al_dev->flashless_boot_on = false;
+
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		struct sdio_channel *ch = &sdio_al_dev->channel[i];
+
+		/* Set default values */
+		ch->read_threshold  = DEFAULT_READ_THRESHOLD;
+		ch->write_threshold = DEFAULT_WRITE_THRESHOLD;
+		ch->min_write_avail = DEFAULT_MIN_WRITE_THRESHOLD;
+		ch->is_packet_mode = true;
+		ch->peer_tx_buf_size = DEFAULT_PEER_TX_BUF_SIZE;
+		ch->poll_delay_msec = 0;
+
+		ch->num = i;
+
+		memset(ch->name, 0, sizeof(ch->name));
+
+		if (header->channel_names[i][0]) {
+			memcpy(ch->name, SDIO_PREFIX,
+			       strlen(SDIO_PREFIX));
+			memcpy(ch->name + strlen(SDIO_PREFIX),
+			       header->channel_names[i],
+			       PEER_CHANNEL_NAME_SIZE);
+
+			ch->is_valid = 1;
+			ch->sdio_al_dev = sdio_al_dev;
+		}
+
+		pr_info(MODULE_NAME ":Channel=%s, is_valid=%d\n", ch->name,
+			ch->is_valid);
+	}
+
+	return 0;
+
+exit_err:
+	sdio_al_get_into_err_state(sdio_al_dev);
+	memset(header, 0, sizeof(*header));
+
+	return -EIO;
+}
+
+/**
+ *  Read SDIO-Client channel configuration
+ *
+ */
+static int read_sdioc_channel_config(struct sdio_channel *ch)
+{
+	int ret;
+	struct peer_sdioc_sw_mailbox *sw_mailbox = NULL;
+	struct peer_sdioc_channel_config *ch_config = NULL;
+	struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev;
+
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": NULL sdio_al_dev for channel %s\n",
+				 ch->name);
+		return -EINVAL;
+	}
+
+	if (sdio_al_dev->sdioc_sw_header->version == 0)
+		return -1;
+
+	pr_debug(MODULE_NAME ":reading sw mailbox %s channel.\n", ch->name);
+
+	sw_mailbox = kzalloc(sizeof(*sw_mailbox), GFP_KERNEL);
+	if (sw_mailbox == NULL)
+		return -ENOMEM;
+
+	ret = sdio_memcpy_fromio(ch->func, sw_mailbox,
+			SDIOC_SW_MAILBOX_ADDR, sizeof(*sw_mailbox));
+	if (ret) {
+		pr_err(MODULE_NAME ":fail to read sw mailbox.\n");
+		goto exit_err;
+	}
+
+	ch_config = &sw_mailbox->ch_config[ch->num];
+	memcpy(&ch->ch_config, ch_config,
+		sizeof(struct peer_sdioc_channel_config));
+
+	if (!ch_config->is_ready) {
+		pr_err(MODULE_NAME ":sw mailbox channel not ready.\n");
+		goto exit_err;
+	}
+
+	pr_info(MODULE_NAME ":ch_config %s max_rx_threshold=%d.\n",
+		ch->name, ch_config->max_rx_threshold);
+	pr_info(MODULE_NAME ":ch_config %s max_tx_threshold=%d.\n",
+		ch->name, ch_config->max_tx_threshold);
+	pr_info(MODULE_NAME ":ch_config %s tx_buf_size=%d.\n",
+		ch->name, ch_config->tx_buf_size);
+
+	/* Aggregation up to 90% of the maximum size */
+	ch->read_threshold = (ch_config->max_rx_threshold * 9) / 10;
+	/* Threshold on 50% of the maximum size , sdioc uses double-buffer */
+	ch->write_threshold = (ch_config->max_tx_threshold * 5) / 10;
+
+	ch->def_read_threshold = ch->read_threshold;
+
+	ch->is_packet_mode = ch_config->is_packet_mode;
+	if (!ch->is_packet_mode) {
+		ch->poll_delay_msec = DEFAULT_POLL_DELAY_NOPACKET_MSEC;
+		ch->min_write_avail = DEFAULT_MIN_WRITE_THRESHOLD_STREAMING;
+	}
+	pr_info(MODULE_NAME ":ch %s is_packet_mode=%d.\n",
+		ch->name, ch->is_packet_mode);
+
+	/* The max_packet_size is set by the modem in version 3 and on */
+	if (sdio_al->sdioc_major > PEER_SDIOC_OLD_VERSION_MAJOR)
+		ch->min_write_avail = ch_config->max_packet_size;
+
+	if (ch->min_write_avail > ch->write_threshold)
+		ch->min_write_avail = ch->write_threshold;
+
+	pr_info(MODULE_NAME ":ch %s read_threshold=%d.\n",
+		ch->name, ch->read_threshold);
+	pr_info(MODULE_NAME ":ch %s write_threshold=%d.\n",
+		ch->name, ch->write_threshold);
+	pr_info(MODULE_NAME ":ch %s def_read_threshold=%d.\n",
+		ch->name, ch->def_read_threshold);
+	pr_info(MODULE_NAME ":ch %s min_write_avail=%d.\n",
+		ch->name, ch->min_write_avail);
+
+	ch->peer_tx_buf_size = ch_config->tx_buf_size;
+
+	kfree(sw_mailbox);
+
+	return 0;
+
+exit_err:
+	pr_info(MODULE_NAME ":Reading SW Mailbox error.\n");
+	kfree(sw_mailbox);
+
+	return -1;
+}
+
+
+/**
+ *  Enable/Disable EOT interrupt of a pipe.
+ *
+ */
+static int enable_eot_interrupt(struct sdio_al_device *sdio_al_dev,
+				int pipe_index, int enable)
+{
+	int ret = 0;
+	struct sdio_func *func1;
+	u32 mask;
+	u32 pipe_mask;
+	u32 addr;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	if (pipe_index < 8) {
+		addr = PIPES_0_7_IRQ_MASK_ADDR;
+		pipe_mask = (1<<pipe_index);
+	} else {
+		addr = PIPES_8_15_IRQ_MASK_ADDR;
+		pipe_mask = (1<<(pipe_index-8));
+	}
+
+	mask = sdio_readl(func1, addr, &ret);
+	if (ret) {
+		pr_debug(MODULE_NAME ":enable_eot_interrupt fail\n");
+		goto exit_err;
+	}
+
+	if (enable)
+		mask &= (~pipe_mask); /* 0 = enable */
+	else
+		mask |= (pipe_mask);  /* 1 = disable */
+
+	sdio_writel(func1, mask, addr, &ret);
+
+exit_err:
+	return ret;
+}
+
+
+/**
+ *  Enable/Disable mask interrupt of a function.
+ *
+ */
+static int enable_mask_irq(struct sdio_al_device *sdio_al_dev,
+			   int func_num, int enable, u8 bit_offset)
+{
+	int ret = 0;
+	struct sdio_func *func1 = NULL;
+	u32 mask = 0;
+	u32 func_mask = 0;
+	u32 addr = 0;
+	u32 offset = 0;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	if (func_num < 4) {
+		addr = FUNC_1_4_MASK_IRQ_ADDR;
+		offset = func_num * 8 + bit_offset;
+	} else {
+		addr = FUNC_5_7_MASK_IRQ_ADDR;
+		offset = (func_num - 4) * 8 + bit_offset;
+	}
+
+	func_mask = 1<<offset;
+
+	mask = sdio_readl(func1, addr, &ret);
+	if (ret) {
+		pr_err(MODULE_NAME ":enable_mask_irq fail\n");
+		goto exit_err;
+	}
+
+	if (enable)
+		mask &= (~func_mask); /* 0 = enable */
+	else
+		mask |= (func_mask);  /* 1 = disable */
+
+	pr_debug(MODULE_NAME ":enable_mask_irq,  writing mask = 0x%x\n", mask);
+
+	sdio_writel(func1, mask, addr, &ret);
+
+exit_err:
+	return ret;
+}
+
+/**
+ *  Enable/Disable Threshold interrupt of a pipe.
+ *
+ */
+static int enable_threshold_interrupt(struct sdio_al_device *sdio_al_dev,
+				      int pipe_index, int enable)
+{
+	int ret = 0;
+	struct sdio_func *func1;
+	u32 mask;
+	u32 pipe_mask;
+	u32 addr;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	if (pipe_index < 8) {
+		addr = PIPES_0_7_IRQ_MASK_ADDR;
+		pipe_mask = (1<<pipe_index);
+	} else {
+		addr = PIPES_8_15_IRQ_MASK_ADDR;
+		pipe_mask = (1<<(pipe_index-8));
+	}
+
+	mask = sdio_readl(func1, addr, &ret);
+	if (ret) {
+		pr_debug(MODULE_NAME ":enable_threshold_interrupt fail\n");
+		goto exit_err;
+	}
+
+	pipe_mask = pipe_mask<<8; /* Threshold bits 8..15 */
+	if (enable)
+		mask &= (~pipe_mask); /* 0 = enable */
+	else
+		mask |= (pipe_mask);  /* 1 = disable */
+
+	sdio_writel(func1, mask, addr, &ret);
+
+exit_err:
+	return ret;
+}
+
+/**
+ *  Set the threshold to trigger interrupt from SDIO-Card on
+ *  pipe available bytes.
+ *
+ */
+static int set_pipe_threshold(struct sdio_al_device *sdio_al_dev,
+			      int pipe_index, int threshold)
+{
+	int ret = 0;
+	struct sdio_func *func1;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	sdio_writel(func1, threshold,
+			PIPES_THRESHOLD_ADDR+pipe_index*4, &ret);
+	if (ret)
+		pr_err(MODULE_NAME ":set_pipe_threshold err=%d\n", -ret);
+
+	return ret;
+}
+
+/**
+ *  Enable func w/ retries
+ *
+ */
+static int sdio_al_enable_func_retry(struct sdio_func *func, const char *name)
+{
+	int ret, i;
+	for (i = 0; i < 200; i++) {
+		ret = sdio_enable_func(func);
+		if (ret) {
+			pr_debug(MODULE_NAME ":retry enable %s func#%d "
+					     "ret=%d\n",
+					 name, func->num, ret);
+			msleep(10);
+		} else
+			break;
+	}
+
+	return ret;
+}
+
+/**
+ *  Open Channel
+ *
+ *  1. Init Channel Context.
+ *  2. Init the Channel SDIO-Function.
+ *  3. Init the Channel Pipes on Mailbox.
+ */
+static int open_channel(struct sdio_channel *ch)
+{
+	int ret = 0;
+	struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev;
+
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": NULL sdio_al_dev for channel %s\n",
+				 ch->name);
+		return -EINVAL;
+	}
+
+	/* Init channel Context */
+	/** Func#1 is reserved for mailbox */
+	ch->func = sdio_al_dev->card->sdio_func[ch->num+1];
+	ch->rx_pipe_index = ch->num*2;
+	ch->tx_pipe_index = ch->num*2+1;
+	ch->signature = SDIO_AL_SIGNATURE;
+
+	ch->total_rx_bytes = 0;
+	ch->total_tx_bytes = 0;
+
+	ch->write_avail = 0;
+	ch->read_avail = 0;
+	ch->rx_pending_bytes = 0;
+
+	mutex_init(&ch->ch_lock);
+
+	pr_debug(MODULE_NAME ":open_channel %s func#%d\n",
+			 ch->name, ch->func->num);
+
+	INIT_LIST_HEAD(&(ch->rx_size_list_head));
+
+	/* Init SDIO Function */
+	ret = sdio_al_enable_func_retry(ch->func, ch->name);
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_enable_func() err=%d\n", -ret);
+		goto exit_err;
+	}
+
+	/* Note: Patch Func CIS tuple issue */
+	ret = sdio_set_block_size(ch->func, SDIO_AL_BLOCK_SIZE);
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_set_block_size()failed, err=%d\n",
+		       -ret);
+		goto exit_err;
+	}
+
+	ch->func->max_blksize = SDIO_AL_BLOCK_SIZE;
+
+	sdio_set_drvdata(ch->func, ch);
+
+	/* Get channel parameters from the peer SDIO-Client */
+	read_sdioc_channel_config(ch);
+
+	/* Set Pipes Threshold on Mailbox */
+	ret = set_pipe_threshold(sdio_al_dev,
+				 ch->rx_pipe_index, ch->read_threshold);
+	if (ret)
+		goto exit_err;
+	ret = set_pipe_threshold(sdio_al_dev,
+				 ch->tx_pipe_index, ch->write_threshold);
+	if (ret)
+		goto exit_err;
+
+	/* Set flag before interrupts are enabled to allow notify */
+	ch->is_open = true;
+
+	sdio_al_dev->poll_delay_msec = get_min_poll_time_msec(sdio_al_dev);
+
+	/* lpm mechanism lives under the assumption there is always a timer */
+	/* Check if need to start the timer */
+	if  ((sdio_al_dev->poll_delay_msec) &&
+	     (sdio_al_dev->is_timer_initialized == false)) {
+
+		init_timer(&sdio_al_dev->timer);
+		sdio_al_dev->timer.data = (unsigned long) sdio_al_dev;
+		sdio_al_dev->timer.function = sdio_al_timer_handler;
+		sdio_al_dev->timer.expires = jiffies +
+			msecs_to_jiffies(sdio_al_dev->poll_delay_msec);
+		add_timer(&sdio_al_dev->timer);
+		sdio_al_dev->is_timer_initialized = true;
+	}
+
+	/* Enable Pipes Interrupts */
+	enable_eot_interrupt(sdio_al_dev, ch->rx_pipe_index, true);
+	enable_eot_interrupt(sdio_al_dev, ch->tx_pipe_index, true);
+
+	enable_threshold_interrupt(sdio_al_dev, ch->rx_pipe_index, true);
+	enable_threshold_interrupt(sdio_al_dev, ch->tx_pipe_index, true);
+
+exit_err:
+
+	return ret;
+}
+
+/**
+ *  Ask the worker to read the mailbox.
+ */
+static void ask_reading_mailbox(struct sdio_al_device *sdio_al_dev)
+{
+	if (!sdio_al_dev->ask_mbox) {
+		pr_debug(MODULE_NAME ":ask_reading_mailbox for card %d\n",
+			 sdio_al_dev->card->host->index);
+		sdio_al_dev->ask_mbox = true;
+		wake_up(&sdio_al_dev->wait_mbox);
+	}
+}
+
+/**
+ *  Start the timer
+ */
+static void start_timer(struct sdio_al_device *sdio_al_dev)
+{
+	if ((sdio_al_dev->poll_delay_msec)  &&
+		(sdio_al_dev->state == CARD_INSERTED)) {
+		sdio_al_dev->timer.expires = jiffies +
+			msecs_to_jiffies(sdio_al_dev->poll_delay_msec);
+		add_timer(&sdio_al_dev->timer);
+	}
+}
+
+/**
+ *  Restart(postpone) the already working timer
+ */
+static void restart_timer(struct sdio_al_device *sdio_al_dev)
+{
+	if ((sdio_al_dev->poll_delay_msec) &&
+		(sdio_al_dev->state == CARD_INSERTED)) {
+		ulong expires =	jiffies +
+			msecs_to_jiffies(sdio_al_dev->poll_delay_msec);
+		mod_timer(&sdio_al_dev->timer, expires);
+	}
+}
+
+/**
+ *  Do the wakup sequence.
+ *  This function should be called after claiming the host!
+ *  The caller is responsible for releasing the host.
+ *
+ *  Wake up sequence
+ *  1. Get lock
+ *  2. Enable wake up function if needed
+ *  3. Mark NOT OK to sleep and write it
+ *  4. Restore default thresholds
+ *  5. Start the mailbox and inactivity timer again
+ */
+static int sdio_al_wake_up(struct sdio_al_device *sdio_al_dev,
+			   u32 not_from_int)
+{
+	int ret = 0, i;
+	struct sdio_func *wk_func =
+		sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1];
+	unsigned long time_to_wait;
+	struct mmc_host *host = wk_func->card->host;
+
+	if (sdio_al_dev->is_err) {
+		SDIO_AL_ERR(__func__);
+		return -ENODEV;
+	}
+
+	/* Wake up sequence */
+	sdio_al_vote_for_sleep(sdio_al_dev, 0);
+	if (not_from_int) {
+		LPM_DEBUG(MODULE_NAME ": Wake up card %d (not by interrupt)",
+			sdio_al_dev->card->host->index);
+	} else {
+		LPM_DEBUG(MODULE_NAME ": Wake up card %d by interrupt",
+			sdio_al_dev->card->host->index);
+	}
+
+	if (!sdio_al_dev->is_ok_to_sleep) {
+		LPM_DEBUG(MODULE_NAME ":card %d already awake, "
+					  "no need to wake up\n",
+			sdio_al_dev->card->host->index);
+		return 0;
+	}
+
+	pr_debug(MODULE_NAME ":Turn clock on for card %d\n",
+		 sdio_al_dev->card->host->index);
+	/* Enable the clock and set its rate */
+	host->ios.clock = sdio_al_dev->clock;
+	msmsdcc_lpm_disable(host);
+	msmsdcc_set_pwrsave(sdio_al_dev->card->host, 0);
+	/* Poll the GPIO */
+	time_to_wait = jiffies + msecs_to_jiffies(1000);
+	while (time_before(jiffies, time_to_wait)) {
+		if (sdio_al->pdata->get_mdm2ap_status())
+			break;
+		udelay(TIME_TO_WAIT_US);
+	}
+	LPM_DEBUG(MODULE_NAME ":GPIO mdm2ap_status=%d\n",
+		       sdio_al->pdata->get_mdm2ap_status());
+
+	/* Here get_mdm2ap_status() returning 0 is not an error condition */
+	if (sdio_al->pdata->get_mdm2ap_status() == 0)
+		LPM_DEBUG(MODULE_NAME ": get_mdm2ap_status() is 0\n");
+
+	/* Enable Wake up Function */
+	ret = sdio_al_enable_func_retry(wk_func, "wakeup func");
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_enable_func() err=%d\n",
+		       -ret);
+		goto error_exit;
+	}
+	/* Mark NOT OK_TOSLEEP */
+	sdio_al_dev->is_ok_to_sleep = 0;
+	ret = write_lpm_info(sdio_al_dev);
+	if (ret) {
+		pr_err(MODULE_NAME ":write_lpm_info() failed, err=%d\n",
+			       -ret);
+		sdio_al_dev->is_ok_to_sleep = 1;
+		sdio_disable_func(wk_func);
+		goto error_exit;
+	}
+
+	/* Restore default thresh for non packet channels */
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		struct sdio_channel *ch = &sdio_al_dev->channel[i];
+		if ((!ch->is_valid) || (!ch->is_open))
+			continue;
+		if (ch->is_packet_mode == false) {
+			ch->read_threshold = ch->def_read_threshold;
+			set_pipe_threshold(sdio_al_dev, ch->rx_pipe_index,
+					   ch->read_threshold);
+		}
+	}
+	sdio_disable_func(wk_func);
+
+	/* Start the timer again*/
+	restart_inactive_time(sdio_al_dev);
+	sdio_al_dev->poll_delay_msec = get_min_poll_time_msec(sdio_al_dev);
+	start_timer(sdio_al_dev);
+
+	LPM_DEBUG(MODULE_NAME "Finished Wake up sequence for card %d",
+		sdio_al_dev->card->host->index);
+
+	msmsdcc_set_pwrsave(sdio_al_dev->card->host, 1);
+	pr_debug(MODULE_NAME ":Turn clock off\n");
+
+	return ret;
+error_exit:
+	sdio_al_vote_for_sleep(sdio_al_dev, 1);
+	msmsdcc_set_pwrsave(sdio_al_dev->card->host, 1);
+	WARN_ON(ret);
+	sdio_al_get_into_err_state(sdio_al_dev);
+	return ret;
+}
+
+
+/**
+ *  SDIO Function Interrupt handler.
+ *
+ *  Interrupt shall be triggered by SDIO-Client when:
+ *  1. End-Of-Transfer (EOT) detected in packet mode.
+ *  2. Bytes-available reached the threshold.
+ *
+ *  Reading the mailbox clears the EOT/Threshold interrupt
+ *  source.
+ *  The interrupt source should be cleared before this ISR
+ *  returns. This ISR is called from IRQ Thread and not
+ *  interrupt, so it may sleep.
+ *
+ */
+static void sdio_func_irq(struct sdio_func *func)
+{
+	struct sdio_al_device *sdio_al_dev = sdio_get_drvdata(func);
+
+	pr_debug(MODULE_NAME ":start %s.\n", __func__);
+
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": NULL sdio_al_dev for card %d\n",
+				 func->card->host->index);
+		return;
+	}
+
+	if (sdio_al_dev->is_ok_to_sleep)
+		sdio_al_wake_up(sdio_al_dev, 0);
+	else
+		restart_timer(sdio_al_dev);
+
+	read_mailbox(sdio_al_dev, true);
+
+	pr_debug(MODULE_NAME ":end %s.\n", __func__);
+}
+
+/**
+ *  Timer Expire Handler
+ *
+ */
+static void sdio_al_timer_handler(unsigned long data)
+{
+	struct sdio_al_device *sdio_al_dev = (struct sdio_al_device *)data;
+	if (sdio_al_dev == NULL) {
+		pr_err(MODULE_NAME ": NULL sdio_al_dev for data %lu\n",
+				 data);
+		return;
+	}
+	if (sdio_al_dev->state != CARD_INSERTED) {
+		pr_err(MODULE_NAME ": sdio_al_dev is in invalid state %d\n",
+				 sdio_al_dev->state);
+		return;
+	}
+	pr_debug(MODULE_NAME " Timer Expired\n");
+
+	ask_reading_mailbox(sdio_al_dev);
+
+	restart_timer(sdio_al_dev);
+}
+
+/**
+ *  Driver Setup.
+ *
+ */
+static int sdio_al_setup(struct sdio_al_device *sdio_al_dev)
+{
+	int ret = 0;
+	struct mmc_card *card = sdio_al_dev->card;
+	struct sdio_func *func1 = NULL;
+	int i = 0;
+	int fn = 0;
+
+	if (card == NULL) {
+		pr_err(MODULE_NAME ":sdio_al_setup: No Card detected\n");
+		return -ENODEV;
+	}
+
+
+	pr_info(MODULE_NAME ":sdio_al_setup for card %d\n",
+		sdio_al_dev->card->host->index);
+
+	func1 = card->sdio_func[0];
+
+	ret = sdio_al->pdata->config_mdm2ap_status(1);
+	if (ret) {
+		pr_err(MODULE_NAME "Could not request GPIO\n");
+		return ret;
+	}
+
+	INIT_WORK(&sdio_al_dev->sdio_al_work.work, worker);
+	/* disable all pipes interrupts before claim irq.
+	   since all are enabled by default. */
+	for (i = 0 ; i < SDIO_AL_MAX_PIPES; i++) {
+		enable_eot_interrupt(sdio_al_dev, i, false);
+		enable_threshold_interrupt(sdio_al_dev, i, false);
+	}
+
+	/* Disable all SDIO Functions before claim irq. */
+	for (fn = 1 ; fn <= card->sdio_funcs; fn++)
+		sdio_disable_func(card->sdio_func[fn-1]);
+
+	sdio_set_drvdata(func1, sdio_al_dev);
+	pr_info(MODULE_NAME ":claim IRQ for card %d\n",
+			card->host->index);
+
+	ret = sdio_claim_irq(func1, sdio_func_irq);
+	if (ret) {
+		pr_err(MODULE_NAME ":Fail to claim IRQ for card %d\n",
+			card->host->index);
+		goto exit_err;
+	}
+
+	sdio_al_dev->is_ready = true;
+
+	/* Start worker before interrupt might happen */
+	queue_work(sdio_al_dev->workqueue, &sdio_al_dev->sdio_al_work.work);
+
+	start_inactive_time(sdio_al_dev);
+
+	pr_debug(MODULE_NAME ":Ready.\n");
+
+	return 0;
+
+exit_err:
+	sdio_release_host(func1);
+	pr_err(MODULE_NAME ":Setup Failure.\n");
+
+	return ret;
+}
+
+/**
+ *  Driver Tear-Down.
+ *
+ */
+static void sdio_al_tear_down(void)
+{
+	int i;
+	struct sdio_al_device *sdio_al_dev = NULL;
+	struct sdio_func *func1;
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) {
+		if (sdio_al->devices[i] == NULL)
+			continue;
+		sdio_al_dev = sdio_al->devices[i];
+
+		if (sdio_al_dev->is_ready) {
+			sdio_al_dev->is_ready = false; /* Flag worker to exit */
+			sdio_al_dev->ask_mbox = false;
+			ask_reading_mailbox(sdio_al_dev); /* Wakeup worker */
+			/* allow gracefully exit of the worker thread */
+			msleep(100);
+
+			flush_workqueue(sdio_al_dev->workqueue);
+			destroy_workqueue(sdio_al_dev->workqueue);
+
+			sdio_al_vote_for_sleep(sdio_al_dev, 1);
+
+			if (sdio_al_verify_func1(sdio_al_dev, __func__)) {
+				pr_err(MODULE_NAME ": %s: Invalid func1",
+				       __func__);
+				return;
+			}
+			func1 = sdio_al_dev->card->sdio_func[0];
+
+			sdio_claim_host(func1);
+			sdio_release_irq(func1);
+			sdio_disable_func(func1);
+			sdio_release_host(func1);
+		}
+	}
+
+	sdio_al->pdata->config_mdm2ap_status(0);
+}
+
+/**
+ *  Find channel by name.
+ *
+ */
+static struct sdio_channel *find_channel_by_name(const char *name)
+{
+	struct sdio_channel *ch = NULL;
+	int i, j;
+	struct sdio_al_device *sdio_al_dev = NULL;
+
+	for (j = 0; j < MAX_NUM_OF_SDIO_DEVICES; ++j) {
+		if (sdio_al->devices[j] == NULL)
+			continue;
+		sdio_al_dev = sdio_al->devices[j];
+		for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+			if (!sdio_al_dev->channel[i].is_valid)
+				continue;
+			if (strcmp(sdio_al_dev->channel[i].name, name) == 0) {
+				ch = &sdio_al_dev->channel[i];
+				break;
+			}
+		}
+		if (ch != NULL)
+			break;
+	}
+
+	return ch;
+}
+
+/**
+ *  Find the minimal poll time.
+ *
+ */
+static int get_min_poll_time_msec(struct sdio_al_device *sdio_sl_dev)
+{
+	int i;
+	int poll_delay_msec = 0x0FFFFFFF;
+
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++)
+		if ((sdio_sl_dev->channel[i].is_valid) &&
+		    (sdio_sl_dev->channel[i].is_open) &&
+		    (sdio_sl_dev->channel[i].poll_delay_msec > 0) &&
+		    (sdio_sl_dev->channel[i].poll_delay_msec < poll_delay_msec))
+			poll_delay_msec =
+				sdio_sl_dev->channel[i].poll_delay_msec;
+
+	if (poll_delay_msec == 0x0FFFFFFF)
+		poll_delay_msec = SDIO_AL_POLL_TIME_NO_STREAMING;
+
+	pr_debug(MODULE_NAME ":poll delay time is %d msec\n", poll_delay_msec);
+
+	return poll_delay_msec;
+}
+
+/**
+ *  Open SDIO Channel.
+ *
+ *  Enable the channel.
+ *  Set the channel context.
+ *  Trigger reading the mailbox to check available bytes.
+ *
+ */
+int sdio_open(const char *name, struct sdio_channel **ret_ch, void *priv,
+		 void (*notify)(void *priv, unsigned ch_event))
+{
+	int ret = 0;
+	struct sdio_channel *ch = NULL;
+	struct sdio_al_device *sdio_al_dev = NULL;
+
+	*ret_ch = NULL; /* default */
+
+	ch = find_channel_by_name(name);
+	if (ch == NULL) {
+		pr_err(MODULE_NAME ":Can't find channel name %s\n", name);
+		return -EINVAL;
+	}
+
+	sdio_al_dev = ch->sdio_al_dev;
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+
+	sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+
+	if (ch->is_open) {
+		pr_err(MODULE_NAME ":Channel already opened %s\n", name);
+		ret = -EPERM;
+		goto exit_err;
+	}
+
+	if (sdio_al_dev->state != CARD_INSERTED) {
+		pr_err(MODULE_NAME ":%s: sdio_al_dev is in invalid state %d\n",
+		       __func__, sdio_al_dev->state);
+		ret = -ENODEV;
+		goto exit_err;
+	}
+
+	if (sdio_al_dev->is_err) {
+		SDIO_AL_ERR(__func__);
+		ret = -ENODEV;
+		goto exit_err;
+	}
+
+	ret = sdio_al_wake_up(sdio_al_dev, 1);
+	if (ret)
+		goto exit_err;
+
+	ch->notify = notify;
+	ch->priv = priv;
+
+	/* Note: Set caller returned context before interrupts are enabled */
+	*ret_ch = ch;
+
+	ret = open_channel(ch);
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_open %s err=%d\n", name, -ret);
+		goto exit_err;
+	}
+
+	pr_info(MODULE_NAME ":sdio_open %s completed OK\n", name);
+	if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) {
+		if (sdio_al->sdioc_major == PEER_SDIOC_OLD_VERSION_MAJOR) {
+			if (!ch->is_packet_mode) {
+				pr_info(MODULE_NAME ":setting channel %s as "
+						    "lpm_chan\n", name);
+				sdio_al_dev->lpm_chan = ch->num;
+			}
+		} else {
+			pr_info(MODULE_NAME ":setting channel %s as lpm_chan\n",
+				name);
+			sdio_al_dev->lpm_chan = ch->num;
+		}
+	}
+
+exit_err:
+	sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+	return ret;
+}
+EXPORT_SYMBOL(sdio_open);
+
+/**
+ *  Close SDIO Channel.
+ *
+ */
+int sdio_close(struct sdio_channel *ch)
+{
+	if (!ch) {
+		pr_err(MODULE_NAME ":%s: NULL channel\n",  __func__);
+		return -ENODEV;
+	}
+	pr_debug(MODULE_NAME ":sdio_close is not supported\n");
+
+	return -EPERM;
+}
+EXPORT_SYMBOL(sdio_close);
+
+/**
+ *  Get the number of available bytes to write.
+ *
+ */
+int sdio_write_avail(struct sdio_channel *ch)
+{
+	if (!ch) {
+		pr_err(MODULE_NAME ":%s: NULL channel\n",  __func__);
+		return -ENODEV;
+	}
+	if (ch->signature != SDIO_AL_SIGNATURE) {
+		pr_err(MODULE_NAME ":%s: Invalid signature 0x%x\n",  __func__,
+			ch->signature);
+		return -ENODEV;
+	}
+
+	pr_debug(MODULE_NAME ":sdio_write_avail %s 0x%x\n",
+			 ch->name, ch->write_avail);
+
+	return ch->write_avail;
+}
+EXPORT_SYMBOL(sdio_write_avail);
+
+/**
+ *  Get the number of available bytes to read.
+ *
+ */
+int sdio_read_avail(struct sdio_channel *ch)
+{
+	if (!ch) {
+		pr_err(MODULE_NAME ":%s: NULL channel\n",  __func__);
+		return -ENODEV;
+	}
+	if (ch->signature != SDIO_AL_SIGNATURE) {
+		pr_err(MODULE_NAME ":%s: Invalid signature 0x%x\n",  __func__,
+			ch->signature);
+		return -ENODEV;
+	}
+
+	pr_debug(MODULE_NAME ":sdio_read_avail %s 0x%x\n",
+			 ch->name, ch->read_avail);
+
+	return ch->read_avail;
+
+}
+EXPORT_SYMBOL(sdio_read_avail);
+
+/**
+ *  Read from SDIO Channel.
+ *
+ *  Reading from the pipe will trigger interrupt if there are
+ *  other pending packets on the SDIO-Client.
+ *
+ */
+int sdio_read(struct sdio_channel *ch, void *data, int len)
+{
+	int ret = 0;
+	struct sdio_al_device *sdio_al_dev = NULL;
+
+	if (!ch) {
+		pr_err(MODULE_NAME ":%s: NULL channel\n",  __func__);
+		return -ENODEV;
+	}
+	if (!data) {
+		pr_err(MODULE_NAME ":%s: NULL data\n",  __func__);
+		return -ENODEV;
+	}
+	if (len == 0) {
+		pr_err(MODULE_NAME ":channel %s trying to read 0 bytes\n",
+		       ch->name);
+		return -EINVAL;
+	}
+
+	if (ch->signature != SDIO_AL_SIGNATURE) {
+		pr_err(MODULE_NAME ":%s: Invalid signature 0x%x\n",  __func__,
+			ch->signature);
+		return -ENODEV;
+	}
+
+	sdio_al_dev = ch->sdio_al_dev;
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+
+	sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+
+	if (sdio_al_dev->is_err) {
+		SDIO_AL_ERR(__func__);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -ENODEV;
+	}
+
+	if (sdio_al_dev->state != CARD_INSERTED) {
+		pr_err(MODULE_NAME ":%s: sdio_al_dev is in invalid state %d\n",
+		       __func__, sdio_al_dev->state);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -ENODEV;
+	}
+
+	/* lpm policy says we can't go to sleep when we have pending rx data,
+	   so either we had rx interrupt and woken up, or we never went to
+	   sleep */
+	if (sdio_al_dev->is_ok_to_sleep) {
+		pr_err(MODULE_NAME ":%s: called when is_ok_to_sleep is set "
+		       "for ch %s, len=%d, last_any_read_avail=%d,"
+		       "last_read_avail=%d, last_old_read_avail=%d",
+		       __func__, ch->name, len,
+		       ch->statistics.last_any_read_avail,
+		       ch->statistics.last_read_avail,
+		       ch->statistics.last_old_read_avail);
+	}
+	BUG_ON(sdio_al_dev->is_ok_to_sleep);
+
+	if (!ch->is_open) {
+		pr_err(MODULE_NAME ":reading from closed channel %s\n",
+				 ch->name);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -EINVAL;
+	}
+
+	DATA_DEBUG(MODULE_NAME ":start ch %s read %d avail %d.\n",
+		ch->name, len, ch->read_avail);
+
+	restart_inactive_time(sdio_al_dev);
+
+	if ((ch->is_packet_mode) && (len != ch->read_avail)) {
+		pr_err(MODULE_NAME ":sdio_read ch %s len != read_avail\n",
+				 ch->name);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -EINVAL;
+	}
+
+	if (len > ch->read_avail) {
+		pr_err(MODULE_NAME ":ERR ch %s: reading more bytes (%d) than"
+				   " the avail(%d).\n",
+				ch->name, len, ch->read_avail);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -ENOMEM;
+	}
+
+	ret = sdio_memcpy_fromio(ch->func, data, PIPE_RX_FIFO_ADDR, len);
+
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_read err=%d, len=%d, read_avail=%d\n",
+		       -ret, len, ch->read_avail);
+		sdio_al_dev->is_err = true;
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return ret;
+	}
+
+	ch->statistics.total_read_times++;
+
+	/* Remove handled packet from the list regardless if ret is ok */
+	if (ch->is_packet_mode)
+		remove_handled_rx_packet(ch);
+	else
+		ch->read_avail -= len;
+
+	ch->total_rx_bytes += len;
+	DATA_DEBUG(MODULE_NAME ":end ch %s read %d avail %d total %d.\n",
+		ch->name, len, ch->read_avail, ch->total_rx_bytes);
+
+	if ((ch->read_avail == 0) && !(ch->is_packet_mode))
+		ask_reading_mailbox(sdio_al_dev);
+
+	sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+
+	return ret;
+}
+EXPORT_SYMBOL(sdio_read);
+
+/**
+ *  Write to SDIO Channel.
+ *
+ */
+int sdio_write(struct sdio_channel *ch, const void *data, int len)
+{
+	int ret = 0;
+	struct sdio_al_device *sdio_al_dev = NULL;
+
+	if (!ch) {
+		pr_err(MODULE_NAME ":%s: NULL channel\n",  __func__);
+		return -ENODEV;
+	}
+	if (!data) {
+		pr_err(MODULE_NAME ":%s: NULL data\n",  __func__);
+		return -ENODEV;
+	}
+	if (len == 0) {
+		pr_err(MODULE_NAME ":channel %s trying to write 0 bytes\n",
+			ch->name);
+		return -EINVAL;
+	}
+
+	if (ch->signature != SDIO_AL_SIGNATURE) {
+		pr_err(MODULE_NAME ":%s: Invalid signature 0x%x\n",  __func__,
+			ch->signature);
+		return -ENODEV;
+	}
+
+	sdio_al_dev = ch->sdio_al_dev;
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+
+	sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+
+
+	if (sdio_al_dev->state != CARD_INSERTED) {
+		pr_err(MODULE_NAME ":%s: sdio_al_dev is in invalid state %d\n",
+		       __func__, sdio_al_dev->state);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -ENODEV;
+	}
+	WARN_ON(len > ch->write_avail);
+
+	if (sdio_al_dev->is_err) {
+		SDIO_AL_ERR(__func__);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -ENODEV;
+	}
+
+	if (!ch->is_open) {
+		pr_err(MODULE_NAME ":writing to closed channel %s\n",
+				 ch->name);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -EINVAL;
+	}
+
+	if (sdio_al_dev->is_ok_to_sleep) {
+		ret = sdio_al_wake_up(sdio_al_dev, 1);
+		if (ret) {
+			sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+			return ret;
+		}
+	} else {
+		restart_inactive_time(sdio_al_dev);
+	}
+
+	DATA_DEBUG(MODULE_NAME ":start ch %s write %d avail %d.\n",
+		ch->name, len, ch->write_avail);
+
+	if (len > ch->write_avail) {
+		pr_err(MODULE_NAME ":ERR ch %s: write more bytes (%d) than "
+				   " available %d.\n",
+				ch->name, len, ch->write_avail);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return -ENOMEM;
+	}
+
+	ret = sdio_ch_write(ch, data, len);
+	if (ret) {
+		pr_err(MODULE_NAME ":sdio_write on channel %s err=%d\n",
+			ch->name, -ret);
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+		return ret;
+	}
+
+	ch->total_tx_bytes += len;
+	DATA_DEBUG(MODULE_NAME ":end ch %s write %d avail %d total %d.\n",
+		ch->name, len, ch->write_avail, ch->total_tx_bytes);
+
+	/* Round up to whole buffer size */
+	len = ROUND_UP(len, ch->peer_tx_buf_size);
+	/* Protect from wraparound */
+	len = min(len, (int) ch->write_avail);
+	ch->write_avail -= len;
+
+	if (ch->write_avail < ch->min_write_avail)
+		ask_reading_mailbox(sdio_al_dev);
+
+	sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+
+	return ret;
+}
+EXPORT_SYMBOL(sdio_write);
+
+static int __devinit msm_sdio_al_probe(struct platform_device *pdev)
+{
+	if (!sdio_al) {
+		pr_err(MODULE_NAME ": %s: NULL sdio_al\n", __func__);
+		return -ENODEV;
+	}
+
+	sdio_al->pdata = pdev->dev.platform_data;
+	return 0;
+}
+
+static int __devexit msm_sdio_al_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver msm_sdio_al_driver = {
+	.probe          = msm_sdio_al_probe,
+	.remove         = __exit_p(msm_sdio_al_remove),
+	.driver         = {
+		.name   = "msm_sdio_al",
+	},
+};
+
+/**
+ *  Initialize SDIO_AL channels.
+ *
+ */
+static int init_channels(struct sdio_al_device *sdio_al_dev)
+{
+	int ret = 0;
+	int i;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+
+	sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+
+	ret = read_sdioc_software_header(sdio_al_dev,
+					 sdio_al_dev->sdioc_sw_header);
+	if (ret)
+		goto exit;
+
+	ret = sdio_al_setup(sdio_al_dev);
+	if (ret)
+		goto exit;
+
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+		int ch_name_size;
+		if (!sdio_al_dev->channel[i].is_valid)
+			continue;
+		if (sdio_al->unittest_mode) {
+			test_channel_init(sdio_al_dev->channel[i].name);
+			memset(sdio_al_dev->channel[i].ch_test_name, 0,
+				sizeof(sdio_al_dev->channel[i].ch_test_name));
+			ch_name_size = strnlen(sdio_al_dev->channel[i].name,
+				       CHANNEL_NAME_SIZE);
+			strncpy(sdio_al_dev->channel[i].ch_test_name,
+			       sdio_al_dev->channel[i].name,
+			       ch_name_size);
+			strncat(sdio_al_dev->channel[i].ch_test_name +
+			       ch_name_size,
+			       SDIO_TEST_POSTFIX,
+			       SDIO_TEST_POSTFIX_SIZE);
+			pr_debug(MODULE_NAME ":pdev.name = %s\n",
+				sdio_al_dev->channel[i].ch_test_name);
+			sdio_al_dev->channel[i].pdev = platform_device_alloc(
+				sdio_al_dev->channel[i].ch_test_name, -1);
+		} else {
+			pr_debug(MODULE_NAME ":pdev.name = %s\n",
+				sdio_al_dev->channel[i].name);
+			sdio_al_dev->channel[i].pdev = platform_device_alloc(
+				sdio_al_dev->channel[i].name, -1);
+		}
+		if (!sdio_al_dev->channel[i].pdev) {
+			pr_err(MODULE_NAME ":NULL platform device for ch %s",
+			       sdio_al_dev->channel[i].name);
+			sdio_al_dev->channel[i].is_valid = 0;
+			continue;
+		}
+		ret = platform_device_add(sdio_al_dev->channel[i].pdev);
+		if (ret) {
+			pr_err(MODULE_NAME ":platform_device_add failed, "
+					   "ret=%d\n", ret);
+			sdio_al_dev->channel[i].is_valid = 0;
+		}
+	}
+
+exit:
+	sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+	return ret;
+}
+
+/**
+ *  Initialize SDIO_AL channels according to the client setup.
+ *  This function also check if the client is in boot mode and
+ *  flashless boot is required to be activated or the client is
+ *  up and running.
+ *
+ */
+static int sdio_al_client_setup(struct sdio_al_device *sdio_al_dev)
+{
+	int ret = 0;
+	struct sdio_func *func1;
+	int signature = 0;
+
+	if (sdio_al_verify_dev(sdio_al_dev, __func__))
+		return -ENODEV;
+	func1 = sdio_al_dev->card->sdio_func[0];
+
+	sdio_claim_host(func1);
+
+	/* Read the header signature to determine the status of the MDM
+	 * SDIO Client
+	 */
+	signature = sdio_readl(func1, SDIOC_SW_HEADER_ADDR, &ret);
+	sdio_release_host(func1);
+	if (ret) {
+		pr_err(MODULE_NAME ":fail to read signature from sw header.\n");
+		return ret;
+	}
+
+	switch (signature) {
+	case PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE:
+		if (sdio_al_dev == sdio_al->bootloader_dev) {
+			pr_info(MODULE_NAME ":setup bootloader on card %d\n",
+				sdio_al_dev->card->host->index);
+			return sdio_al_bootloader_setup();
+		} else {
+			pr_info(MODULE_NAME ":wait for bootloader completion "
+					    "on card %d\n",
+				sdio_al_dev->card->host->index);
+			return sdio_al_wait_for_bootloader_comp(sdio_al_dev);
+		}
+	case PEER_SDIOC_SW_MAILBOX_SIGNATURE:
+	case PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE:
+		return init_channels(sdio_al_dev);
+	default:
+		pr_err(MODULE_NAME ":Invalid signature 0x%x\n", signature);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * SDIO driver functions
+ */
+static int sdio_al_sdio_probe(struct sdio_func *func,
+		const struct sdio_device_id *sdio_dev_id)
+{
+	int ret = 0;
+	struct sdio_al_device *sdio_al_dev = NULL;
+	int i;
+	struct mmc_card *card = NULL;
+
+	if (!func) {
+		pr_err(MODULE_NAME ": %s: NULL func\n", __func__);
+		return -ENODEV;
+	}
+	card = func->card;
+
+	if (!card) {
+		pr_err(MODULE_NAME ": %s: NULL card\n", __func__);
+		return -ENODEV;
+	}
+
+	if (card->sdio_funcs < SDIO_AL_MAX_FUNCS) {
+		dev_info(&card->dev,
+			 "SDIO-functions# %d less than expected.\n",
+			 card->sdio_funcs);
+		return -ENODEV;
+	}
+
+	/* Check if there is already a device for this card */
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) {
+		if (sdio_al->devices[i] == NULL)
+			continue;
+		if (sdio_al->devices[i]->card == card)
+			return 0;
+	}
+
+	dev_info(&card->dev, "SDIO Card claimed.\n");
+
+	sdio_al_dev = kzalloc(sizeof(struct sdio_al_device), GFP_KERNEL);
+	if (sdio_al_dev == NULL)
+		return -ENOMEM;
+
+	sdio_al_dev->state = CARD_INSERTED;
+
+	if (card->host->index == SDIO_BOOTLOADER_CARD_INDEX)
+		sdio_al->bootloader_dev = sdio_al_dev;
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES ; ++i)
+		if (sdio_al->devices[i] == NULL) {
+			sdio_al->devices[i] = sdio_al_dev;
+			break;
+		}
+	if (i == MAX_NUM_OF_SDIO_DEVICES) {
+		pr_err(MODULE_NAME ":No space in devices array for the "
+				   "device\n");
+		return -ENOMEM;
+	}
+
+	sdio_al_dev->is_ready = false;
+
+	sdio_al_dev->signature = SDIO_AL_SIGNATURE;
+
+	sdio_al_dev->is_suspended = 0;
+	sdio_al_dev->is_timer_initialized = false;
+
+	sdio_al_dev->lpm_chan = INVALID_SDIO_CHAN;
+
+	sdio_al_dev->card = card;
+
+	sdio_al_dev->mailbox = kzalloc(sizeof(struct sdio_mailbox), GFP_KERNEL);
+	if (sdio_al_dev->mailbox == NULL)
+		return -ENOMEM;
+
+	sdio_al_dev->sdioc_sw_header
+		= kzalloc(sizeof(*sdio_al_dev->sdioc_sw_header), GFP_KERNEL);
+	if (sdio_al_dev->sdioc_sw_header == NULL)
+		return -ENOMEM;
+
+	sdio_al_dev->timer.data = (unsigned long)sdio_al_dev;
+
+	wake_lock_init(&sdio_al_dev->wake_lock, WAKE_LOCK_SUSPEND, MODULE_NAME);
+	/* Don't allow sleep until all required clients register */
+	sdio_al_vote_for_sleep(sdio_al_dev, 0);
+
+	sdio_claim_host(card->sdio_func[0]);
+
+	/* Init Func#1 */
+	ret = sdio_enable_func(card->sdio_func[0]);
+	if (ret) {
+		pr_err(MODULE_NAME ":Fail to enable Func#%d\n",
+		       card->sdio_func[0]->num);
+		goto exit;
+	}
+
+	/* Patch Func CIS tuple issue */
+	ret = sdio_set_block_size(card->sdio_func[0], SDIO_AL_BLOCK_SIZE);
+	if (ret) {
+		pr_err(MODULE_NAME ":Fail to set block size, Func#%d\n",
+			card->sdio_func[0]->num);
+		goto exit;
+	}
+	sdio_al_dev->card->sdio_func[0]->max_blksize = SDIO_AL_BLOCK_SIZE;
+
+	sdio_al_dev->workqueue = create_singlethread_workqueue("sdio_al_wq");
+	sdio_al_dev->sdio_al_work.sdio_al_dev = sdio_al_dev;
+	init_waitqueue_head(&sdio_al_dev->wait_mbox);
+
+	ret = sdio_al_client_setup(sdio_al_dev);
+
+exit:
+	sdio_release_host(card->sdio_func[0]);
+	return ret;
+}
+
+static void sdio_al_sdio_remove(struct sdio_func *func)
+{
+	struct sdio_al_device *sdio_al_dev = NULL;
+	int i;
+	int state;
+	struct mmc_card *card = NULL;
+
+	if (!func) {
+		pr_err(MODULE_NAME ": %s: NULL func\n", __func__);
+		return;
+	}
+	card = func->card;
+
+	if (!card) {
+		pr_err(MODULE_NAME ": %s: NULL card\n", __func__);
+		return;
+	}
+
+	/* Find the sdio_al_device of this card */
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) {
+		if (sdio_al->devices[i] == NULL)
+			continue;
+		if (sdio_al->devices[i]->card == card) {
+			sdio_al_dev = sdio_al->devices[i];
+			sdio_al->devices[i] = NULL;
+			break;
+		}
+	}
+	if (sdio_al_dev == NULL) {
+		pr_debug(MODULE_NAME ":%s :NULL sdio_al_dev for card %d\n",
+				 __func__, card->host->index);
+		return;
+	}
+
+	pr_info(MODULE_NAME ":%s for card %d\n",
+			 __func__, card->host->index);
+
+	if (card->sdio_func[0])
+		sdio_claim_host(card->sdio_func[0]);
+	else
+		pr_err(MODULE_NAME ":%s: NULL func1 for card %d\n",
+			 __func__, card->host->index);
+
+	if (sdio_al_dev->state == CARD_REMOVED)
+		return;
+
+	state = sdio_al_dev->state;
+	sdio_al_dev->state = CARD_REMOVED;
+
+	for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++)
+		sdio_al_dev->channel[i].signature = 0x0;
+
+	pr_info(MODULE_NAME ":%s: ask_reading_mailbox for card %d\n",
+			 __func__, card->host->index);
+	sdio_al_dev->is_ready = false; /* Flag worker to exit */
+	sdio_al_dev->ask_mbox = false;
+	ask_reading_mailbox(sdio_al_dev); /* Wakeup worker */
+
+	if (state != MODEM_RESTART) {
+		if (sdio_al_dev->is_timer_initialized) {
+			pr_info(MODULE_NAME ": %s: Stop timer for card %d",
+				__func__, sdio_al_dev->card->host->index);
+			sdio_al_dev->poll_delay_msec = 0;
+			del_timer_sync(&sdio_al_dev->timer);
+		}
+
+		if (!sdio_al->unittest_mode) {
+			pr_info(MODULE_NAME ":%s: notifying clients for "
+					    "card %d\n",
+					 __func__, card->host->index);
+			for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) {
+				if (!sdio_al_dev->channel[i].is_valid)
+					continue;
+				platform_device_unregister(
+					sdio_al_dev->channel[i].pdev);
+				sdio_al_dev->channel[i].signature = 0x0;
+			}
+		}
+	}
+	if (card->sdio_func[0])
+		sdio_release_host(card->sdio_func[0]);
+
+	pr_info(MODULE_NAME ":%s: vote for sleep for card %d\n",
+			 __func__, card->host->index);
+	sdio_al_vote_for_sleep(sdio_al_dev, 1);
+
+	pr_info(MODULE_NAME ":%s: flush_workqueue for card %d\n",
+			 __func__, card->host->index);
+	flush_workqueue(sdio_al_dev->workqueue);
+	destroy_workqueue(sdio_al_dev->workqueue);
+	wake_lock_destroy(&sdio_al_dev->wake_lock);
+
+	pr_info(MODULE_NAME ":%s: delete data structures for card %d\n",
+			 __func__, card->host->index);
+	kfree(sdio_al_dev->sdioc_sw_header);
+	kfree(sdio_al_dev->mailbox);
+	kfree(sdio_al_dev);
+
+	pr_info(MODULE_NAME ":%s: sdio card %d removed.\n", __func__,
+		card->host->index);
+}
+
+static void sdio_print_mailbox(char *prefix_str, struct sdio_mailbox *mailbox)
+{
+	int k = 0;
+	char buf[256];
+	char buf1[10];
+
+	if (!mailbox) {
+		pr_err(MODULE_NAME ": mailbox is NULL\n");
+		return;
+	}
+
+	pr_err(MODULE_NAME ": %s: pipes 0_7: eot=0x%x, "
+		"thresh=0x%x, overflow=0x%x, "
+		"underflow=0x%x, mask_thresh=0x%x\n",
+		 prefix_str, mailbox->eot_pipe_0_7,
+		 mailbox->thresh_above_limit_pipe_0_7,
+		 mailbox->overflow_pipe_0_7,
+		 mailbox->underflow_pipe_0_7,
+		 mailbox->mask_thresh_above_limit_pipe_0_7);
+
+	memset(buf, 0, sizeof(buf));
+	strncat(buf, ": bytes_avail:", sizeof(buf));
+
+	for (k = 0 ; k < SDIO_AL_ACTIVE_PIPES ; ++k) {
+		snprintf(buf1, sizeof(buf1), "%d, ",
+			 mailbox->pipe_bytes_avail[k]);
+		strncat(buf, buf1, sizeof(buf));
+	}
+
+	pr_err(MODULE_NAME "%s", buf);
+}
+
+static void sdio_al_print_info(void)
+{
+	int i = 0;
+	int j = 0;
+	int ret = 0;
+	struct sdio_mailbox *mailbox = NULL;
+	struct sdio_mailbox *hw_mailbox = NULL;
+	struct peer_sdioc_channel_config *ch_config = NULL;
+	struct sdio_func *func1 = NULL;
+	struct sdio_func *lpm_func = NULL;
+	int offset = 0;
+	int is_ok_to_sleep = 0;
+	static atomic_t first_time;
+	char buf[50];
+
+	if (atomic_read(&first_time) == 1)
+		return;
+
+	atomic_set(&first_time, 1);
+
+	pr_err(MODULE_NAME ": %s - SDIO DEBUG INFO\n", __func__);
+
+	if (!sdio_al) {
+		pr_err(MODULE_NAME ": %s - ERROR - sdio_al is NULL\n",
+			 __func__);
+		return;
+	}
+
+	pr_err(MODULE_NAME ": GPIO mdm2ap_status=%d\n",
+				sdio_al->pdata->get_mdm2ap_status());
+
+	for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) {
+		struct sdio_al_device *sdio_al_dev = sdio_al->devices[j];
+
+		if (sdio_al_dev == NULL) {
+			continue;
+		}
+
+		if (!sdio_al_dev->card && !sdio_al_dev->card->host) {
+			pr_err(MODULE_NAME ": Card or Host fields "
+			       "are NULL\n);");
+			continue;
+		}
+
+		snprintf(buf, sizeof(buf), "Card#%d: Shadow HW MB",
+		       sdio_al_dev->card->host->index);
+
+		/* printing Shadowing HW Mailbox*/
+		mailbox = sdio_al_dev->mailbox;
+		sdio_print_mailbox(buf, mailbox);
+
+		pr_err(MODULE_NAME ": Card#%d: "
+			"is_ok_to_sleep=%d\n",
+			sdio_al_dev->card->host->index,
+			sdio_al_dev->is_ok_to_sleep);
+
+
+		pr_err(MODULE_NAME ": Card#%d: "
+				   "Shadow channels SW MB:",
+		       sdio_al_dev->card->host->index);
+
+		/* printing Shadowing SW Mailbox per channel*/
+		for (i = 0 ; i < SDIO_AL_MAX_CHANNELS ; ++i) {
+			struct sdio_channel *ch = &sdio_al_dev->channel[i];
+
+			if (ch == NULL) {
+				continue;
+			}
+
+			if (!ch->is_valid) {
+				continue;
+			}
+
+			ch_config = &sdio_al_dev->channel[i].ch_config;
+
+			pr_err(MODULE_NAME ": Ch %s: max_rx_thres=0x%x, "
+				"max_tx_thres=0x%x, tx_buf=0x%x, "
+				"is_packet_mode=%d, "
+				"max_packet=0x%x, min_write=0x%x",
+				ch->name, ch_config->max_rx_threshold,
+				ch_config->max_tx_threshold,
+				ch_config->tx_buf_size,
+				ch_config->is_packet_mode,
+				ch_config->max_packet_size,
+				ch->min_write_avail);
+
+			if (!ch->is_open) {
+				pr_err(MODULE_NAME
+					 ": %s is VALID but NOT OPEN. "
+					"continuing...", ch->name);
+				continue;
+			}
+
+			pr_err(MODULE_NAME ": total_rx=0x%x, "
+				"total_tx=0x%x, "
+				"read_avail=0x%x, "
+				"write_avail=0x%x, rx_pending=0x%x, "
+				"num_reads=0x%x, num_notifs=0x%x",
+				ch->total_rx_bytes, ch->total_tx_bytes,
+				ch->read_avail, ch->write_avail,
+				ch->rx_pending_bytes,
+				ch->statistics.total_read_times,
+				ch->statistics.total_notifs);
+		} /* end loop over all channels */
+
+	} /* end loop over all devices */
+
+	/* reading from client and printing is_host_ok_to_sleep per device */
+	for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) {
+		struct sdio_al_device *sdio_al_dev = sdio_al->devices[j];
+
+		if (sdio_al_verify_dev(sdio_al_dev, __func__))
+			return;
+
+		if (!sdio_al_dev->card->host) {
+			pr_err(MODULE_NAME ": Host is NULL");
+			continue;
+		}
+
+		if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) {
+			pr_err(MODULE_NAME ": %s - for "
+			       "Card#%d, is lpm_chan=="
+			       "INVALID_SDIO_CHAN. continuing...",
+			       __func__, sdio_al_dev->card->host->index);
+			continue;
+		}
+
+		offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config)+
+		sizeof(struct peer_sdioc_channel_config) *
+		sdio_al_dev->lpm_chan+
+		offsetof(struct peer_sdioc_channel_config, is_host_ok_to_sleep);
+
+		lpm_func = sdio_al_dev->card->sdio_func[sdio_al_dev->
+								lpm_chan+1];
+		if (!lpm_func) {
+			pr_err(MODULE_NAME ": %s - lpm_func is NULL for card#%d"
+			       " continuing...\n", __func__,
+			       sdio_al_dev->card->host->index);
+			continue;
+		}
+
+		sdio_claim_host(sdio_al_dev->card->sdio_func[0]);
+		ret  =  sdio_memcpy_fromio(lpm_func,
+					    &is_ok_to_sleep,
+					    SDIOC_SW_MAILBOX_ADDR+offset,
+					    sizeof(int));
+		sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+
+		if (ret)
+			pr_err(MODULE_NAME ": %s - fail to read "
+				"is_HOST_ok_to_sleep from mailbox for card %d",
+				__func__, sdio_al_dev->card->host->index);
+		else
+			pr_err(MODULE_NAME ": Card#%d: "
+				"is_HOST_ok_to_sleep=%d\n",
+				sdio_al_dev->card->host->index,
+				is_ok_to_sleep);
+	}
+
+	for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) {
+		struct sdio_al_device *sdio_al_dev = sdio_al->devices[j];
+
+		if (sdio_al_verify_dev(sdio_al_dev, __func__))
+			return;
+
+		if (!sdio_al_dev->card->host) {
+			pr_err(MODULE_NAME ": Host is NULL");
+			continue;
+		}
+
+		/* Reading HW Mailbox */
+		hw_mailbox = sdio_al_dev->mailbox;
+		func1 = sdio_al_dev->card->sdio_func[0];
+
+		sdio_claim_host(func1);
+		ret = sdio_memcpy_fromio(func1, hw_mailbox,
+			HW_MAILBOX_ADDR, sizeof(*hw_mailbox));
+		sdio_release_host(func1);
+
+		if (ret) {
+			pr_err(MODULE_NAME ": fail to read "
+			       "mailbox for card#%d. "
+			       "continuing...\n",
+			       sdio_al_dev->card->host->index);
+			continue;
+		}
+
+		snprintf(buf, sizeof(buf), "Card#%d: Current HW MB",
+		       sdio_al_dev->card->host->index);
+
+		/* Printing HW Mailbox */
+		sdio_print_mailbox(buf, hw_mailbox);
+	}
+}
+
+static struct sdio_device_id sdio_al_sdioid[] = {
+    {.class = 0, .vendor = 0x70, .device = 0x2460},
+    {.class = 0, .vendor = 0x70, .device = 0x0460},
+    {.class = 0, .vendor = 0x70, .device = 0x23F1},
+    {.class = 0, .vendor = 0x70, .device = 0x23F0},
+    {}
+};
+
+static struct sdio_driver sdio_al_sdiofn_driver = {
+    .name      = "sdio_al_sdiofn",
+    .id_table  = sdio_al_sdioid,
+    .probe     = sdio_al_sdio_probe,
+    .remove    = sdio_al_sdio_remove,
+};
+
+#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
+/*
+ *  Callback for notifications from restart mudule.
+ *  This function handles only the BEFORE_RESTART notification.
+ *  Stop all the activity on the card and notify our clients.
+ */
+static int sdio_al_subsys_notifier_cb(struct notifier_block *this,
+				  unsigned long notif_type,
+				  void *data)
+{
+	int i, j;
+	struct sdio_func *func1 = NULL;
+	int ret;
+
+	if (notif_type != SUBSYS_BEFORE_SHUTDOWN) {
+		pr_info(MODULE_NAME ": %s: got notification %ld",
+			__func__, notif_type);
+		return NOTIFY_DONE;
+	}
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) {
+		struct sdio_al_device *sdio_al_dev = NULL;
+		if (sdio_al->devices[i] == NULL) {
+			pr_debug(MODULE_NAME ": %s: NULL device in index %d",
+			__func__, i);
+			continue;
+		}
+		sdio_al_dev = sdio_al->devices[i];
+		if (sdio_al_dev->state == CARD_REMOVED) {
+			pr_info(MODULE_NAME ": %s: card %d is already removed",
+				__func__, sdio_al_dev->card->host->index);
+			continue;
+		}
+		if (sdio_al_dev->state == MODEM_RESTART) {
+			pr_info(MODULE_NAME ": %s: card %d was already "
+					    "notified for modem reset",
+				__func__, sdio_al_dev->card->host->index);
+			continue;
+		}
+
+		pr_info(MODULE_NAME ": %s: Set the state to MODEM_RESTART"
+			" for card %d",
+			__func__, sdio_al_dev->card->host->index);
+		sdio_al_dev->state = MODEM_RESTART;
+		sdio_al_dev->is_ready = false;
+
+		/* Stop mailbox timer */
+		if (sdio_al_dev->is_timer_initialized) {
+			pr_debug(MODULE_NAME ": %s: Stop timer for card %d",
+				__func__, sdio_al_dev->card->host->index);
+			sdio_al_dev->poll_delay_msec = 0;
+			del_timer_sync(&sdio_al_dev->timer);
+		}
+	}
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) {
+		struct sdio_al_device *sdio_al_dev;
+		if (sdio_al->devices[i] == NULL) {
+			pr_debug(MODULE_NAME ": %s: NULL device in index %d",
+			__func__, i);
+			continue;
+		}
+		sdio_al_dev = sdio_al->devices[i];
+
+		if (!sdio_al_verify_func1(sdio_al_dev, __func__)) {
+			func1 = sdio_al_dev->card->sdio_func[0];
+			sdio_claim_host(func1);
+
+			if ((sdio_al_dev->is_ok_to_sleep) &&
+			    (!sdio_al_dev->is_err)) {
+				pr_debug(MODULE_NAME ": %s: wakeup modem for "
+						    "card %d", __func__,
+					sdio_al_dev->card->host->index);
+				ret = sdio_al_wake_up(sdio_al_dev, 1);
+				if (ret == 0) {
+					pr_info(MODULE_NAME ": %s: "
+							    "sdio_release_irq"
+							    " for card %d",
+						__func__,
+						sdio_al_dev->card->host->index);
+					sdio_release_irq(func1);
+				}
+			} else {
+				pr_debug(MODULE_NAME ": %s: sdio_release_irq"
+						    " for card %d",
+					__func__,
+					sdio_al_dev->card->host->index);
+				sdio_release_irq(func1);
+			}
+		}
+
+		pr_debug(MODULE_NAME ": %s: Notifying SDIO clients for card %d",
+				__func__, sdio_al_dev->card->host->index);
+		if (!sdio_al->unittest_mode)
+			for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) {
+				if (!sdio_al_dev->channel[j].is_valid)
+					continue;
+				platform_device_unregister(
+					sdio_al_dev->channel[j].pdev);
+				sdio_al_dev->channel[i].signature = 0x0;
+			}
+
+		if (!sdio_al_verify_func1(sdio_al_dev, __func__))
+			sdio_release_host(sdio_al_dev->card->sdio_func[0]);
+
+		pr_debug(MODULE_NAME ": %s: Allows sleep for card %d", __func__,
+			sdio_al_dev->card->host->index);
+		sdio_al_vote_for_sleep(sdio_al_dev, 1);
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block sdio_al_nb = {
+	.notifier_call = sdio_al_subsys_notifier_cb,
+};
+#endif
+
+/**
+ *  Module Init.
+ *
+ *  @warn: allocate sdio_al context before registering driver.
+ *
+ */
+static int __init sdio_al_init(void)
+{
+	int ret = 0;
+	int i;
+
+	pr_debug(MODULE_NAME ":sdio_al_init\n");
+
+	pr_info(MODULE_NAME ":SDIO-AL SW version %s\n",
+		DRV_VERSION);
+
+	sdio_al = kzalloc(sizeof(struct sdio_al), GFP_KERNEL);
+	if (sdio_al == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES ; ++i)
+		sdio_al->devices[i] = NULL;
+
+	sdio_al->unittest_mode = false;
+
+	sdio_al->debug.debug_lpm_on = debug_lpm_on;
+	sdio_al->debug.debug_data_on = debug_data_on;
+
+#ifdef CONFIG_DEBUG_FS
+	sdio_al_debugfs_init();
+#endif
+
+
+#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
+	sdio_al->subsys_notif_handle = subsys_notif_register_notifier(
+		"external_modem", &sdio_al_nb);
+#endif
+
+	ret = platform_driver_register(&msm_sdio_al_driver);
+	if (ret) {
+		pr_err(MODULE_NAME ": platform_driver_register failed: %d\n",
+		       ret);
+		goto exit;
+	}
+
+	sdio_register_driver(&sdio_al_sdiofn_driver);
+exit:
+	if (ret)
+		kfree(sdio_al);
+	return ret;
+}
+
+/**
+ *  Module Exit.
+ *
+ *  Free allocated memory.
+ *  Disable SDIO-Card.
+ *  Unregister driver.
+ *
+ */
+static void __exit sdio_al_exit(void)
+{
+	if (sdio_al == NULL)
+		return;
+
+	pr_debug(MODULE_NAME ":sdio_al_exit\n");
+
+#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
+	subsys_notif_unregister_notifier(
+		sdio_al->subsys_notif_handle, &sdio_al_nb);
+#endif
+
+	sdio_al_tear_down();
+
+	sdio_unregister_driver(&sdio_al_sdiofn_driver);
+
+	kfree(sdio_al);
+
+#ifdef CONFIG_DEBUG_FS
+	sdio_al_debugfs_cleanup();
+#endif
+
+	platform_driver_unregister(&msm_sdio_al_driver);
+
+	pr_debug(MODULE_NAME ":sdio_al_exit complete\n");
+}
+
+module_init(sdio_al_init);
+module_exit(sdio_al_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SDIO Abstraction Layer");
+MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
+MODULE_VERSION(DRV_VERSION);
+