public inbox for linux-omap@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] MMC support for Palm Tungsten|T
@ 2006-10-19 16:28 Marek Vašut
  0 siblings, 0 replies; only message in thread
From: Marek Vašut @ 2006-10-19 16:28 UTC (permalink / raw)
  To: linux-omap-open-source

[-- Attachment #1: Type: text/plain, Size: 251 bytes --]

Hi,
this patch adds mmc support for palmTT. It has to be applied to the previous 
one (basic palmtt support). It changes palmtt_defconfig too, so mmc is 
enabled by default.

br
Marek Vasut

Signed-off-by: Marek Vašut <marek.vasut@gmail.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: palmtt-mmc.patch --]
[-- Type: text/x-diff; charset="us-ascii"; name="palmtt-mmc.patch", Size: 46417 bytes --]

diff -Naur linux-omap-current/arch/arm/configs/palmtt_defconfig linux-omap/arch/arm/configs/palmtt_defconfig
--- linux-omap-current/arch/arm/configs/palmtt_defconfig	2006-10-19 13:50:26.000000000 +0200
+++ linux-omap/arch/arm/configs/palmtt_defconfig	2006-10-19 18:02:36.000000000 +0200
@@ -1,7 +1,7 @@
 #

 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.18-omap1
-# Thu Oct 19 11:55:31 2006
+# Thu Oct 19 15:32:16 2006
 #
 CONFIG_ARM=y
 CONFIG_MMU=y
@@ -28,8 +28,11 @@
 CONFIG_LOCALVERSION_AUTO=y
 CONFIG_SWAP=y
 CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
 CONFIG_BSD_PROCESS_ACCT=y
 # CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
 # CONFIG_IKCONFIG is not set
 # CONFIG_RELAY is not set
 CONFIG_INITRAMFS_SOURCE=""
@@ -252,7 +255,82 @@
 #
 # Networking
 #
-# CONFIG_NET is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_BIC=y
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
 
 #
 # Device Drivers
@@ -269,6 +347,7 @@
 #
 # Connector - unified userspace <-> kernelspace linker
 #
+# CONFIG_CONNECTOR is not set
 
 #
 # Memory Technology Devices (MTD)
@@ -289,9 +368,11 @@
 #
 # CONFIG_BLK_DEV_COW_COMMON is not set
 # CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_INITRD is not set
 # CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
 
 #
 # SCSI device support
@@ -318,8 +399,41 @@
 #
 
 #
+# Network device support
+#
+# CONFIG_NETDEVICES is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# PHY device support
+#
+
+#
+# Ethernet (10 or 100Mbit)
+#
+# CONFIG_NET_ETHERNET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
 # ISDN subsystem
 #
+# CONFIG_ISDN is not set
 
 #
 # Input device support
@@ -468,6 +582,7 @@
 #
 # Digital Video Broadcasting Devices
 #
+# CONFIG_DVB is not set
 
 #
 # Graphics support
@@ -541,7 +656,11 @@
 #
 # MMC/SD Card support
 #
-# CONFIG_MMC is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_BLOCK=y
+# CONFIG_MMC_OMAP is not set
+CONFIG_MMC_PALMTT=y
 
 #
 # Real Time Clock
@@ -571,6 +690,7 @@
 # CONFIG_JFS_FS is not set
 # CONFIG_FS_POSIX_ACL is not set
 # CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_ROMFS_FS is not set
 # CONFIG_INOTIFY is not set
@@ -621,6 +741,18 @@
 # CONFIG_UFS_FS is not set
 
 #
+# Network File Systems
+#
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
 # Partition Types
 #
 # CONFIG_PARTITION_ADVANCED is not set
diff -Naur linux-omap-current/drivers/mmc/Kconfig linux-omap/drivers/mmc/Kconfig
--- linux-omap-current/drivers/mmc/Kconfig	2006-10-19 13:50:44.000000000 +0200
+++ linux-omap/drivers/mmc/Kconfig	2006-10-19 14:40:53.000000000 +0200
@@ -71,6 +71,16 @@
 
 	  If unsure, say N.
 
+config MMC_PALMTT
+	tristate "Palm Tungsten|T Multimedia Card Interface support"
+	depends on ARCH_OMAP && MMC
+	help
+	  This selects the Multimedia card Interface on Palm Tungsten|T.
+	  This driver works with MMC card in SPI mode connected to OMAP
+	  McBSP. If you have such PDA, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_WBSD
 	tristate "Winbond W83L51xD SD/MMC Card Interface support"
 	depends on MMC && ISA_DMA_API
diff -Naur linux-omap-current/drivers/mmc/Makefile linux-omap/drivers/mmc/Makefile
--- linux-omap-current/drivers/mmc/Makefile	2006-10-19 13:50:44.000000000 +0200
+++ linux-omap/drivers/mmc/Makefile	2006-10-19 14:41:24.000000000 +0200
@@ -22,6 +22,7 @@
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
+obj-$(CONFIG_MMC_PALMTT)	+= palmtt.o
 obj-$(CONFIG_MMC_AT91RM9200)	+= at91_mci.o
 
 mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
diff -Naur linux-omap-current/drivers/mmc/palmtt.c linux-omap/drivers/mmc/palmtt.c
--- linux-omap-current/drivers/mmc/palmtt.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-omap/drivers/mmc/palmtt.c	2006-10-19 18:07:58.000000000 +0200
@@ -0,0 +1,1541 @@
+/*
+ *  linux/drivers/mmc/palmtt.c
+ *
+ *  Copyright (C) 2004 Nokia Corporation
+ *  Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
+ *  Pin multiplexing and Innovator support by Tony Lindgren <tony@atomide.com>
+ *  Changes to make it work on PalmTT2 by Nikolay Petukhov
+ *  Changes to make it work on PalmTT by Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+#include <linux/mmc/card.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/fpga.h>
+#include <asm/arch/tps65010.h>
+#include <asm/arch/menelaus.h>
+
+#include <asm/arch/mcbsp.h>
+
+#include <asm/arch/clock.h>
+
+#define DRIVER_NAME "mmci-palmtt"
+
+#undef PALMTT_CONFIG_MMC_DEBUG
+//#define PALMTT_CONFIG_MMC_DEBUG
+
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+//#define DBG(x...)	printk(KERN_DEBUG x)
+#define DBG(x...)	printk(x)
+#else
+#define DBG(x...)	do { } while (0)
+#endif
+
+/* Specifies how often in millisecs to poll for card status changes
+ * when the cover switch is open */
+#define OMAP_MMC_SWITCH_POLL_DELAY     5000
+
+#define MYW 0
+#define MYR 1
+
+struct mmc_palmtt_host {
+	int                     initialized;
+	int			suspended;
+	struct mmc_request *	mrq;
+	struct mmc_command *	cmd;
+	struct mmc_data *	data;
+	struct mmc_host *	mmc;
+	struct device *		dev;
+	unsigned char		id;
+	struct clk *		clk;
+	u32			base;
+//	int irq;
+	unsigned char		bus_mode;
+	unsigned int		dma_len;
+#define OMAP_MMC_DATADIR_NONE	0
+#define OMAP_MMC_DATADIR_READ	1
+#define OMAP_MMC_DATADIR_WRITE	2
+	unsigned char		datadir;
+
+	int                     power_pin;
+	int			switch_pin;
+	struct work_struct	switch_work;
+	struct timer_list	switch_timer;
+	int			switch_last_state;
+	int                     cs_pin;
+
+	struct omap_mcbsp_spi_cfg spi_cfg;
+	long			io_base;
+
+	unsigned		use_dma;
+
+	int                     dma_ch_tx,dma_ch_rx;
+	unsigned char*		write_buffer;
+	unsigned char*		read_buffer;
+	dma_addr_t		p_dma_write;
+	dma_addr_t		p_dma_read;
+	struct completion       dma_completion;
+	struct completion       dma_completion_rx;
+
+	struct clk *		mcbsp_clk;
+	unsigned char*		buffer;
+	u32			bytesleft;
+	int			ocr;
+	int			cid[4];
+	int 			addr;
+	struct completion       pause;
+	unsigned char		bug;
+	unsigned char		tx_bug;
+	unsigned char		undef;
+};
+
+
+static void mmc_palmtt_cs(struct mmc_palmtt_host *host, int on)
+{
+	if (on) {
+	    omap_set_gpio_dataout(host->cs_pin, 1);
+	} else {
+	    omap_set_gpio_dataout(host->cs_pin, 0);
+	}
+}
+
+static void mmc_palmtt_power(struct mmc_palmtt_host *host, int on)
+{
+	if (on) {
+	    omap_set_gpio_dataout(host->power_pin, 0);
+	} else {
+	    omap_set_gpio_dataout(host->power_pin, 1);
+	}
+}
+
+static inline int
+mmc_palmtt_cover_is_open(struct mmc_palmtt_host *host)
+{
+        return omap_get_gpio_datain(host->switch_pin);
+//return 1;
+}
+
+static ssize_t
+mmc_palmtt_show_cover_switch(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct mmc_palmtt_host *host = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", mmc_palmtt_cover_is_open(host) ? "open" : "closed");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_palmtt_show_cover_switch, NULL);
+
+static irqreturn_t mmc_palmtt_switch_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mmc_palmtt_host *host = (struct mmc_palmtt_host *) dev_id;
+
+	DBG("MMC%d cover is now %s\n", host->id,
+	    mmc_palmtt_cover_is_open(host) ? "open" : "closed");
+	schedule_work(&host->switch_work);
+
+	return IRQ_HANDLED;
+}
+
+static void mmc_palmtt_switch_timer(unsigned long arg)
+{
+	struct mmc_palmtt_host *host = (struct mmc_palmtt_host *) arg;
+
+	schedule_work(&host->switch_work);
+}
+
+static void mmc_palmtt_switch_handler(void *data)
+{
+	struct mmc_palmtt_host *host = (struct mmc_palmtt_host *) data;
+	struct mmc_card *card;
+	static int complained = 0;
+	int cards = 0, cover_open;
+
+	//printk("MMC cover switch handler started\n");
+
+	cover_open = mmc_palmtt_cover_is_open(host);
+
+	if (cover_open != host->switch_last_state) {
+		kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
+		host->switch_last_state = cover_open;
+
+		mmc_detect_change(host->mmc, 0);
+		list_for_each_entry(card, &host->mmc->cards, node) {
+		    if (mmc_card_present(card))
+			cards++;
+		}
+		DBG("MMC%d: %d card(s) present\n", host->id, cards);
+	}
+
+	if (mmc_palmtt_cover_is_open(host)) {
+		if (!complained) {
+			DBG(KERN_INFO "MMC%d: cover is open\n", host->id);
+			complained = 1;
+		}
+		mod_timer(&host->switch_timer,
+			  jiffies + OMAP_MMC_SWITCH_POLL_DELAY * HZ / 1000);
+		host->ocr=0;
+		host->addr=0;
+	} else {
+		complained = 0;
+	}
+}
+
+static inline int is_broken_card(struct mmc_card *card)
+{
+	int i;
+	struct mmc_cid *c = &card->cid;
+	static const struct broken_card_cid {
+		unsigned int manfid;
+		char prod_name[8];
+		unsigned char hwrev;
+		unsigned char fwrev;
+	} broken_cards[] = {
+		{ 0x00150000, "\x30\x30\x30\x30\x30\x30\x15\x00", 0x06, 0x03 },
+	};
+
+	for (i = 0; i < sizeof(broken_cards)/sizeof(broken_cards[0]); i++) {
+		const struct broken_card_cid *b = broken_cards + i;
+
+		if (b->manfid != c->manfid)
+			continue;
+		if (memcmp(b->prod_name, c->prod_name, sizeof(b->prod_name)) != 0)
+			continue;
+		if (b->hwrev != c->hwrev || b->fwrev != c->fwrev)
+			continue;
+		return 1;
+	}
+	return 0;
+}
+
+static void palmtt_dma_callback_tx(int lch, u16 ch_status, void *data)
+{
+	struct mmc_palmtt_host *host = (struct mmc_palmtt_host *) data;
+
+//	DBG("DMA callback TX: 0x%x\n", OMAP_MCBSP_READ(host->io_base, SPCR2));
+
+	/* FIXME: We ignore the possible errors for now. */
+	if (host->dma_ch_tx < 0) {
+		DBG("DMA TX callback while DMA not enabled?\n",
+		       host->id);
+		return;
+	}
+	omap_free_dma(host->dma_ch_tx);
+	host->dma_ch_tx = -1;
+
+	complete(&host->dma_completion);
+}
+
+static void palmtt_dma_callback_rx(int lch, u16 ch_status, void *data)
+{
+	struct mmc_palmtt_host *host = (struct mmc_palmtt_host *) data;
+        int dma_ch_rx;
+
+//	DBG("DMA callback RX: 0x%x\n", OMAP_MCBSP_READ(host->io_base, SPCR1));
+
+	/* FIXME: We ignore the possible errors for now. */
+	if (host->dma_ch_rx < 0) {
+		DBG("DMA RX callback while DMA not enabled?\n",
+		       host->id);
+		return;
+	}
+	dma_ch_rx = host->dma_ch_rx;
+	host->dma_ch_rx = -1;
+
+	omap_free_dma(dma_ch_rx);
+	complete(&host->dma_completion_rx);
+}
+
+
+int palmtt_dma_xmit(struct mmc_palmtt_host *host,  dma_addr_t buffer, unsigned int length)
+{
+	int dma_ch_tx,ret;
+	short w;
+
+	if (host->bug)
+	    return 0;
+
+	w = OMAP_MCBSP_READ(host->io_base, SPCR2);
+	OMAP_MCBSP_WRITE(host->io_base, SPCR2, w & ~0x01);
+
+	if (omap_request_dma(OMAP_DMA_MCBSP2_TX, "McBSP TX", palmtt_dma_callback_tx, host, &dma_ch_tx)) {
+		printk("OMAP-McBSP: Unable to request DMA channel for McBSP2 TX. Trying IRQ based TX\n");
+		return -EAGAIN;
+	}
+
+//	DBG("TX DMA on channel %d\n", dma_ch_tx);
+
+	init_completion(&(host->dma_completion));
+
+	omap_set_dma_dest_params(dma_ch_tx,
+				 OMAP_DMA_PORT_TIPB,
+				 OMAP_DMA_AMODE_CONSTANT,
+				 host->io_base + OMAP_MCBSP_REG_DXR1, 0, 0);
+
+	omap_set_dma_src_params(dma_ch_tx,
+				OMAP_DMA_PORT_EMIFF,
+				OMAP_DMA_AMODE_POST_INC,
+				buffer, 0, 0);
+
+	omap_set_dma_transfer_params(dma_ch_tx,
+				     OMAP_DMA_DATA_TYPE_S8,
+				     length, 1,
+				     OMAP_DMA_SYNC_ELEMENT, 0, 0);
+
+
+	host->dma_ch_tx = dma_ch_tx;
+
+	w = OMAP_MCBSP_READ(host->io_base, SPCR2);
+	OMAP_MCBSP_WRITE(host->io_base, SPCR2, w | 0x1);
+
+	omap_start_dma(dma_ch_tx);
+
+	if ( (ret = wait_for_completion_timeout(&host->dma_completion,3))==0 ) {
+	    if ( host->dma_ch_tx !=-1  ) {
+		printk("free\n");
+		omap_free_dma(dma_ch_tx);
+		host->dma_ch_tx = -1;
+	    printk("-----------DMA TX BUG-----------\n");
+	    }
+	    else
+		printk("******* DMA TX BUG **********\n");
+	    complete(&host->dma_completion);
+	    host->bug++;
+	}
+
+	return 0;
+}
+
+//int palmtt_recv_buffer(struct mmc_palmtt_host *host, unsigned char* buffer,unsigned char* dest_buffer, unsigned int length , unsigned char r)
+unsigned char* palmtt_dma_recv(struct mmc_palmtt_host *host, dma_addr_t buffer,dma_addr_t dest_buffer, unsigned int length , unsigned char r)
+{
+	int dma_ch_tx,dma_ch_rx,i,ret;
+	short w;
+
+        host->tx_bug=0;
+
+	if (host->bug)
+	    return 0;
+
+	w = OMAP_MCBSP_READ(host->io_base, SPCR2);
+	OMAP_MCBSP_WRITE(host->io_base, SPCR2, w & ~0x01);
+
+	if (r) {
+	for (i=0;i<length+1;i++) {
+	    host->write_buffer[i]=0xff;
+	    host->read_buffer[i]=0xa;
+	}
+	}
+
+	if (omap_request_dma(OMAP_DMA_MCBSP2_TX, "McBSP TX", palmtt_dma_callback_tx, host, &dma_ch_tx)) {
+		printk("OMAP-McBSP: Unable to request DMA channel for McBSP2 TX. Trying IRQ based TX\n");
+//		return -EAGAIN;
+		return 0;
+	}
+
+	omap_set_dma_dest_params(dma_ch_tx,
+				 OMAP_DMA_PORT_TIPB,
+				 OMAP_DMA_AMODE_CONSTANT,
+				 host->io_base + OMAP_MCBSP_REG_DXR1, 0, 0);
+
+	omap_set_dma_src_params(dma_ch_tx,
+				OMAP_DMA_PORT_EMIFF,
+				OMAP_DMA_AMODE_POST_INC,
+				buffer, 0, 0);
+
+	omap_set_dma_transfer_params(dma_ch_tx,
+				     OMAP_DMA_DATA_TYPE_S8,
+//				     OMAP_DMA_DATA_TYPE_S16,
+				     length, 1,
+				     OMAP_DMA_SYNC_ELEMENT, 0, 0);
+
+
+	if (omap_request_dma(OMAP_DMA_MCBSP2_RX, "McBSP RX", palmtt_dma_callback_rx, host, &dma_ch_rx)) {
+		printk("OMAP-McBSP: Unable to request DMA channel for McBSP2 RX. Trying IRQ based TX\n");
+		return 0;
+//		return -EAGAIN;
+	}
+
+	omap_set_dma_dest_params(dma_ch_rx,
+				 OMAP_DMA_PORT_EMIFF,
+				 OMAP_DMA_AMODE_POST_INC,
+				 dest_buffer, 0, 0);
+
+	omap_set_dma_src_params(dma_ch_rx,
+				OMAP_DMA_PORT_TIPB,
+				OMAP_DMA_AMODE_CONSTANT,
+				host->io_base + OMAP_MCBSP_REG_DRR1, 0, 0);
+
+	omap_set_dma_transfer_params(dma_ch_rx,
+				     OMAP_DMA_DATA_TYPE_S8,
+//				     OMAP_DMA_DATA_TYPE_S16,
+				     length, 1,
+				     OMAP_DMA_SYNC_ELEMENT, 0, 0);
+
+	init_completion(&(host->dma_completion));
+	init_completion(&(host->dma_completion_rx));
+
+	host->dma_ch_tx = dma_ch_tx;
+	host->dma_ch_rx = dma_ch_rx;
+
+	w = OMAP_MCBSP_READ(host->io_base, SPCR2);
+	OMAP_MCBSP_WRITE(host->io_base, SPCR2, w | 0x1);
+
+	OMAP_MCBSP_READ(host->io_base, DRR1);
+
+	omap_start_dma(dma_ch_rx);
+
+	omap_start_dma(dma_ch_tx);
+
+	if ( (ret = wait_for_completion_timeout(&host->dma_completion,3))==0 ) {
+	    if ( host->dma_ch_tx !=-1  ) {
+		omap_free_dma(dma_ch_tx);
+		host->dma_ch_tx = -1;
+	    }
+	    else{
+	        host->tx_bug=1;
+	        init_completion(&host->pause);
+	        wait_for_completion_timeout(&host->pause,1);
+	        complete(&host->pause);
+	    }
+	}
+
+	if ( (ret = wait_for_completion_timeout(&host->dma_completion_rx,3))==0 ) {
+	    omap_free_dma(dma_ch_rx);
+	    host->dma_ch_rx = -1;
+	    complete(&host->dma_completion_rx);
+	}
+	
+	if(host->tx_bug)
+	    complete(&host->dma_completion);
+
+	if (host->bug)
+	    return 0;
+
+	return host->read_buffer;
+}
+
+
+int palmtt_irq_xmit(struct mmc_palmtt_host *host, unsigned int length) {
+    int i;
+    for (i=0;i<length;i++)
+	omap_mcbsp_xmit_word( 2, host->write_buffer[i]);
+
+    return 0;
+}
+unsigned char * palmtt_irq_recv(struct mmc_palmtt_host *host, unsigned int length ) {
+    int i;
+
+    for (i=0;i<length;i++) {
+	omap_mcbsp_xmit_word( 2, host->write_buffer[i]);
+	host->read_buffer[i] = ((omap_mcbsp_recv_word( 2 ))&0x0f);
+    }
+
+    return host->read_buffer;
+}
+
+int palmtt_xmit(struct mmc_palmtt_host *host, unsigned int length) {
+    return palmtt_dma_recv(host, host->p_dma_write,  host->p_dma_read, length , MYW);
+//    return palmtt_dma_xmit(host, host->p_dma_write, length);
+}
+
+
+unsigned char * palmtt_recv(struct mmc_palmtt_host *host, unsigned int length) {
+    return palmtt_dma_recv(host, host->p_dma_write,  host->p_dma_read, length , MYR);
+}
+
+
+
+
+static int
+mmc_palmtt_response(struct mmc_palmtt_host *host, int length_xmit, int length_find,unsigned char find)
+{
+	int i;
+	int ret=-1;
+	char *buffer;
+
+	buffer = palmtt_recv(host, length_xmit);
+	if (!buffer)
+	    return -1;
+
+#ifdef __PALMTT_CONFIG_MMC_DEBUG
+	if (length_xmit <30) {
+	    printk("ans:");
+	    for (i=0; i<length_xmit; i++)
+		printk(" %x",buffer[i]);
+	    printk("\n");
+	}
+#endif
+
+	for (i=0;i<length_find;i++) {
+	    if ( buffer[i]==find ) {
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+		printk("FOUND %x at %i\n",find,i);
+#endif
+		ret=i;
+		break;
+	    }
+	}
+	if(ret==-1) {
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+		printk("%x NOT FOUND\n",find);
+#endif
+	}
+
+    return ret;
+}
+
+static int
+mmc_palmtt_load_data(struct mmc_palmtt_host *host, int length_xmit)
+{
+	char *buffer;
+
+	buffer = palmtt_recv(host, length_xmit);
+	if (!buffer)
+	    return -1;
+
+    return 0;
+}
+
+static int
+mmc_palmtt_read_ocr(struct mmc_palmtt_host *host, struct mmc_command *cmd)
+{
+    int i,ret=0;
+    mmc_palmtt_cs(host, 0);
+		
+    //cmd
+    host->write_buffer[0]=(58|0x40);
+//arg
+    host->write_buffer[1]=0x0;
+    host->write_buffer[2]=0x0;
+    host->write_buffer[3]=0x0;
+    host->write_buffer[4]=0x0;
+//crc
+    host->write_buffer[5]=0x95;
+
+    palmtt_xmit(host,6);
+
+    if ( (i=mmc_palmtt_response(host, 7, 3, 0) )!=-1 ) {
+//	cmd->error = MMC_ERR_NONE;
+	ret = (host->read_buffer[i+3]) | (host->read_buffer[i+3])<<8 | (host->read_buffer[i+2])<<16 | (host->read_buffer[i+1])<<24;
+	return ret;
+    }
+
+    cmd->error = MMC_ERR_TIMEOUT;
+    ret=0;
+    return ret;
+}
+
+/*
+static void
+mmc_palmtt_crc_off(struct mmc_palmtt_host *host, struct mmc_command *cmd)
+{
+//    int i,ret=0;
+    mmc_palmtt_cs(host, 0);
+		
+    //cmd
+    host->dma_write[0]=(59|0x40);
+//arg
+    host->dma_write[1]=0x0;
+    host->dma_write[2]=0x0;
+    host->dma_write[3]=0x0;
+    host->dma_write[4]=0x0;
+//crc
+    host->dma_write[5]=0x95;
+
+    palmtt_xmit_buffer(host, host->p_dma_write, 6);
+
+    palmtt_recv_buffer(host, host->p_dma_write,host->p_dma_read, 1 , MYR);
+
+
+    if (host->dma_read[0]==0)
+	cmd->error = MMC_ERR_NONE;
+    else
+	cmd->error = MMC_ERR_TIMEOUT;
+
+    return;
+    
+//    mmc_palmtt_cs(host, 1);
+
+}
+*/
+static void mmc_palmtt_prepare_data(struct mmc_palmtt_host *host, struct mmc_request *req)
+{
+	struct mmc_data *data = req->data;
+
+	host->data = data;
+	if (data == NULL)
+		return;
+
+//	DBG("MMC%d: Data xfer (%s %s), DTO %d cycles + %d ns, %d blocks of %d bytes\n",
+//	    host->id, (data->flags & MMC_DATA_STREAM) ? "stream" : "block",
+//	    (data->flags & MMC_DATA_WRITE) ? "write" : "read",
+//	    data->timeout_clks, data->timeout_ns, data->blocks,
+//	    1 << data->blksz_bits);
+
+	host->datadir = (data->flags & MMC_DATA_WRITE) ?
+		OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
+	host->dma_len = 0;
+
+	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+				   host->datadir);
+
+	/* No SG-DMA */
+	if (unlikely(host->dma_len > 1))
+		BUG();
+//	DBG("host->dma_len:%x data->sg_len:%x data->sg->length:%x\n",host->dma_len, data->sg_len, data->sg->length);
+	
+	host->buffer = page_address(data->sg->page) + data->sg->offset;
+	host->bytesleft = data->blocks * (1 << data->blksz_bits);
+
+}
+
+static void
+mmc_palmtt_cmd_done(struct mmc_palmtt_host *host, struct mmc_command *cmd)
+{
+
+	if (cmd->flags & MMC_RSP_136) {
+		// response type 2 
+		cmd->resp[3] = host->cid[3];
+		cmd->resp[2] = host->cid[2];
+		cmd->resp[1] = host->cid[1];
+		cmd->resp[0] = host->cid[0];
+		DBG("MMC%d: Response %08x %08x %08x %08x\n", host->id,
+		    cmd->resp[0], cmd->resp[1],
+		    cmd->resp[2], cmd->resp[3]);
+	}
+	else {
+
+		    if ( cmd->opcode == MMC_SEND_OP_COND && cmd->error == MMC_ERR_NONE ) {
+			cmd->resp[0] = mmc_palmtt_read_ocr(host, cmd);
+//			host->ocr=cmd->resp[0];
+		    }
+		    else if ( cmd->error != MMC_ERR_NONE )
+			cmd->resp[0]=0xffff;
+		    else 
+			cmd->resp[0]=0;
+
+		    if ( cmd->opcode == MMC_SEND_STATUS && cmd->error == MMC_ERR_NONE )
+			cmd->resp[0] |= R1_READY_FOR_DATA;
+
+		DBG("MMC%d: Response %08x\n", host->id, cmd->resp[0]);
+	}
+
+	if (host->data) {
+	    host->datadir = OMAP_MMC_DATADIR_NONE;
+
+	    if (host->data->error == MMC_ERR_NONE)
+		host->data->bytes_xfered += host->data->blocks * (1<<host->data->blksz_bits);
+
+	    dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
+		     host->datadir);
+	    host->dma_len = 0;
+	    host->data = NULL;
+	}
+
+
+//	if ( !host->bug && (host->data->error = MMC_ERR_NONE) ) {
+		DBG("MMC%d: End request, err %x\n", host->id, cmd->error);
+		host->cmd = NULL;
+		host->mrq = NULL;
+		mmc_request_done(host->mmc, cmd->mrq);
+//	}
+
+}
+
+
+static int
+mmc_palmtt_analyze_response(char *buffer,int length_find,unsigned char find, char mask)
+{
+	int i;
+	int ret=-1;
+	for (i=0;i<length_find;i++) {
+	    if ( (buffer[i]&mask)==find ) {
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+		printk("FOUND %x at %i\n",find,i);
+#endif
+		ret=i;
+		break;
+	    }
+	}
+	if(ret==-1) {
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+		printk("%x NOT FOUND\n",find);
+#endif
+	}
+	return ret;
+}
+
+
+static void
+mmc_palmtt_command(struct mmc_palmtt_host *host,unsigned char cmd, unsigned int arg)
+{
+	//cmd
+	host->write_buffer[0]=(cmd|0x40);
+	
+	//arg
+	host->write_buffer[1]=arg>>24;
+	host->write_buffer[2]=arg>>16;
+	host->write_buffer[3]=arg>>8;
+	host->write_buffer[4]=arg;
+	//crc
+	host->write_buffer[5]=0x95;
+
+	palmtt_xmit(host, 6);
+}
+
+int
+mmc_palmtt_command_response(struct mmc_palmtt_host *host,unsigned char cmd, unsigned int arg, int length_find,unsigned char find)
+{
+	int ret=-1,i=0;
+
+	//cmd
+	host->write_buffer[0]=(cmd|0x40);
+	
+	//arg
+	host->write_buffer[1]=arg>>24;
+	host->write_buffer[2]=arg>>16;
+	host->write_buffer[3]=arg>>8;
+	host->write_buffer[4]=arg;
+	//crc
+	host->write_buffer[5]=0x95;
+
+	for (i=0;i<length_find;i++)
+	    host->write_buffer[i+6]=0xff;
+
+	palmtt_xmit(host, 6 + length_find);
+//	if (!buffer)
+//	    return -1;
+
+#ifdef __PALMTT_CONFIG_MMC_DEBUG
+	if (length_find <30) {
+	    printk("ans:");
+	    for (i=0; i<length_find; i++)
+		printk(" %x",host->read_buffer[6+i]);
+	    printk("\n");
+	}
+#endif
+
+	for (i=0;i<length_find;i++) {
+	    if ( host->read_buffer[6+i]==find ) {
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+		printk("FOUND %x at %i\n",find,i);
+#endif
+		ret=i+6;
+		break;
+	    }
+	}
+	if(ret==-1) {
+#ifdef PALMTT_CONFIG_MMC_DEBUG
+		printk("%x NOT FOUND\n",find);
+#endif
+	}
+
+    return ret;
+
+
+}
+
+
+static void
+mmc_palmtt_start_command(struct mmc_palmtt_host *host, struct mmc_command *cmd)
+{
+	int i,j;
+	int retry,ret;
+	unsigned int addr;
+	cmd->error = MMC_ERR_TIMEOUT;
+	host->cmd = cmd;
+
+omap_set_gpio_dataout(3,1);
+
+	switch (cmd->opcode) {
+
+//-------------CMD0---------------
+	    case MMC_GO_IDLE_STATE:
+		mmc_palmtt_cs(host, 1);
+		mmc_palmtt_power(host, 1);
+		host->addr=0;
+		
+		//80clks
+		for (i = 0; i < 10; i++)
+		    host->write_buffer[i]=0xff;
+
+		palmtt_xmit(host, 10);
+		mmc_palmtt_cs(host, 0);
+		
+		mmc_palmtt_command(host, cmd->opcode , cmd->arg);
+
+		for (i = 0; i < 15; i++) {
+
+		    if ( (ret=mmc_palmtt_response(host, 2, 2, 1) )!=-1 ) {
+			cmd->error = MMC_ERR_NONE;
+			break;
+		    }
+		}
+
+//		mmc_palmtt_cs(host, 1);
+		break;
+
+
+//-------------CMD1---------------
+	    case MMC_SEND_OP_COND:
+
+		mmc_palmtt_cs(host, 0);
+		
+		retry=0;
+		
+		do {
+		    
+		    mmc_palmtt_command(host, cmd->opcode , cmd->arg);
+
+		    if ( (ret=mmc_palmtt_response(host, 2, 2, 0) )!=-1 ) {
+			cmd->error = MMC_ERR_NONE;
+			retry=25;
+		    }
+		    retry++;
+
+		} while (retry<24);
+		
+//		if (cmd->error == MMC_ERR_NONE)
+//		    mmc_palmtt_crc_off(host,cmd);
+
+//		mmc_palmtt_cs(host, 1);
+		break;//CMD1
+
+
+//-------------CMD2---------------
+	    case  MMC_ALL_SEND_CID: //CMD2 not support in SPI mode
+	    case  MMC_SEND_CID: //CMD10
+	    case  MMC_SEND_CSD: //CMD9
+
+		mmc_palmtt_cs(host, 0);
+		
+		if (cmd->opcode==MMC_ALL_SEND_CID) {
+		    if (host->addr) {
+			DBG("Card add before\n");
+//			cmd->error = MMC_ERR_NONE;
+			host->undef=1;
+			break;
+		    }
+		    mmc_palmtt_command(host, MMC_SEND_CID , cmd->arg);
+		}
+		else
+		    mmc_palmtt_command(host, cmd->opcode , cmd->arg);
+
+
+		if ( (ret=mmc_palmtt_response(host, 26, 2, 0) )!=-1 ) {
+		
+//			cmd->error = MMC_ERR_NONE;
+
+//		    i = mmc_palmtt_response(host, 23, 2, 0xfe);
+		    if (host->read_buffer[ret+1]==0xfe)
+			i=ret+1;
+		    else if (host->read_buffer[ret+2]==0xfe)
+			i=ret+2;
+		    else i=-1;
+//			i=6;
+//		    if (i=3)
+		    if (i>0) { //start single block
+		    //Why on my SD CARD CSD_STRUCTURE==0	
+		    if (cmd->opcode==MMC_SEND_CSD)
+			host->read_buffer[i+1]|=0x8c;
+
+host->cid[0] =(host->read_buffer[i+1])<<24|(host->read_buffer[i+2])<<16|(host->read_buffer[i+3])<<8|(host->read_buffer[i+4]);
+host->cid[1] =(host->read_buffer[i+5])<<24|(host->read_buffer[i+6])<<16|(host->read_buffer[i+7])<<8|(host->read_buffer[i+8]);
+host->cid[2] =(host->read_buffer[i+9])<<24|(host->read_buffer[i+10])<<16|(host->read_buffer[i+11])<<8|(host->read_buffer[i+12]);
+host->cid[3] =(host->read_buffer[i+13])<<24|(host->read_buffer[i+14])<<16|(host->read_buffer[i+15])<<8|(host->read_buffer[i+16]);
+
+			cmd->error = MMC_ERR_NONE;
+		    }
+		    break;
+		}
+		else {
+		    host->cid[0]=0;
+		    host->cid[1]=0;
+		    host->cid[2]=0;
+		    host->cid[3]=0;
+		}
+//		mmc_palmtt_cs(host, 1);
+		break;
+
+
+//-------------CMD3---------------
+	    case  MMC_SET_RELATIVE_ADDR:
+		cmd->error = MMC_ERR_NONE;
+		host->addr=(cmd->arg)>>16;
+		break;
+
+
+//-------------CMD7---------------
+	    case  MMC_SELECT_CARD:
+		if ( ( host->addr == (cmd->arg)>>16 ) || ( (cmd->arg)>>16 ) == 0 )
+		    cmd->error = MMC_ERR_NONE;
+
+		break;
+
+
+//-------------CMD13---------------
+	    case  MMC_SEND_STATUS:
+
+		mmc_palmtt_command(host, cmd->opcode , cmd->arg);
+
+		if ( (ret=mmc_palmtt_response(host, 4, 4, 0) )!=-1 ) {
+		    if (host->read_buffer[ret+1]==0)
+			cmd->error = MMC_ERR_NONE;
+		}
+
+		break;
+
+
+//-------------CMD16---------------
+	    case  MMC_SET_BLOCKLEN:
+/*
+		mmc_palmtt_command(host, cmd->opcode , cmd->arg);
+
+		    if ( (ret=mmc_palmtt_response(host, 2, 2, 0) )!=-1 ) {
+			cmd->error = MMC_ERR_NONE;
+			break;
+		    }
+*/
+		ret = mmc_palmtt_command_response(host, cmd->opcode , cmd->arg, 2, 0);
+		
+		    if ( ret != -1 ) {
+			cmd->error = MMC_ERR_NONE;
+			break;
+		    }
+
+		break;
+
+
+//-------------CMD17---------------
+	    case  MMC_READ_SINGLE_BLOCK:
+	    
+		host->data->error = MMC_ERR_TIMEOUT;
+		
+		    addr=cmd->arg;
+		    
+		    cmd->error = MMC_ERR_TIMEOUT;
+
+		    ret = mmc_palmtt_command_response(host, MMC_READ_SINGLE_BLOCK, addr, 10, 0);
+		
+			if ( ret == -1 ) {
+			    DBG("ans:%x %x %x %x %x %x %x %x %x %x\n",
+			    host->read_buffer[5], host->read_buffer[6],host->read_buffer[7],host->read_buffer[8],host->read_buffer[9],
+			    host->read_buffer[10], host->read_buffer[11],host->read_buffer[12],host->read_buffer[13],host->read_buffer[14]);
+			    break;
+			}
+			
+		    cmd->error = MMC_ERR_NONE;
+
+		for (j=0;j<host->data->blocks;j++) {
+		    //before read block data, need wait 1ms
+		    init_completion(&host->pause);
+		    wait_for_completion_timeout(&host->pause,1);
+		    complete(&host->pause);
+		    do {
+			mmc_palmtt_load_data(host, 1);
+		    } while (host->read_buffer[0]!=0xfe);
+		    mmc_palmtt_load_data(host, 514);
+		    memcpy(host->buffer+512*j,&(host->read_buffer[0]), 512); //?512
+
+		}//for (j=0;j<host->data->blocks;j++)
+			
+		    if (host->data->error == MMC_ERR_TIMEOUT)
+			DBG("ans:%x %x %x %x ... %x %x %x %x\n",
+			host->read_buffer[0], host->read_buffer[1], host->read_buffer[2], host->read_buffer[3],
+			host->read_buffer[511], host->read_buffer[512],host->read_buffer[513],host->read_buffer[514]);
+
+
+		break;
+
+//-------------CMD18---------------
+	    case  MMC_READ_MULTIPLE_BLOCK:
+	    
+		host->data->error = MMC_ERR_TIMEOUT;
+		addr=cmd->arg;
+
+//		printk("CMD18:%d blocks\n",host->data->blocks);
+
+		ret = mmc_palmtt_command_response(host, MMC_READ_MULTIPLE_BLOCK, cmd->arg, 10, 0);
+		
+		cmd->error = MMC_ERR_TIMEOUT;
+		if ( ret == -1 ) {
+		    printk("CMD18 ans:%x %x %x %x %x %x %x %x %x %x\n",
+		    host->read_buffer[5], host->read_buffer[6],host->read_buffer[7],host->read_buffer[8],host->read_buffer[9],
+		    host->read_buffer[10], host->read_buffer[11],host->read_buffer[12],host->read_buffer[13],host->read_buffer[14]);
+		    break;
+		}
+
+		cmd->error = MMC_ERR_NONE;
+
+		    //before read block data, need wait 1ms
+		    init_completion(&host->pause);
+		    wait_for_completion_timeout(&host->pause,1);
+		    complete(&host->pause);
+
+		for (j=0;j<host->data->blocks;j++) {
+		    do {
+			mmc_palmtt_load_data(host, 1);
+		    } while (host->read_buffer[0]!=0xfe);
+		    mmc_palmtt_load_data(host, 514);
+		    memcpy(host->buffer+512*j,&(host->read_buffer[0]), 512); //?512
+
+		}//for (j=0;j<host->data->blocks;j++)
+
+		host->data->error = MMC_ERR_NONE;
+
+		ret = mmc_palmtt_command_response(host, MMC_STOP_TRANSMISSION, 0, 10, 0);
+		
+		if ( ret == -1 ) {
+		    DBG("STOP ans:%x %x %x %x %x %x %x %x %x %x\n",
+		    host->read_buffer[5], host->read_buffer[6],host->read_buffer[7],host->read_buffer[8],host->read_buffer[9],
+		    host->read_buffer[10], host->read_buffer[11],host->read_buffer[12],host->read_buffer[13],host->read_buffer[14]);
+		    cmd->error = MMC_ERR_TIMEOUT;
+		    break;
+		}
+
+		break;
+
+//-------------CMD24---------------
+	    case  MMC_WRITE_BLOCK:
+//	    	printk("*****************WRITING****************\n");
+		host->data->error = MMC_ERR_TIMEOUT;
+		host->data->flags = MMC_DATA_WRITE;		
+		addr=cmd->arg;
+		    
+
+		cmd->error = MMC_ERR_TIMEOUT;
+
+		ret = mmc_palmtt_command_response(host, MMC_WRITE_BLOCK, addr, 8, 0);
+
+		
+		if ( ret == -1 ) {
+			break;
+			}
+	ret=mmc_palmtt_analyze_response(&(host->read_buffer[6]), 7, 0, (char)0xff);
+	if (ret>-1)
+	{
+		do
+		{
+			for (i=0;i<8;i++)
+			    host->write_buffer[i]=0xff;
+			palmtt_xmit(host, 8 );
+			ret=mmc_palmtt_analyze_response(host->read_buffer, 8, 0, (char)0xff);
+		}
+		while (ret>-1);
+	}
+
+			
+		cmd->error = MMC_ERR_NONE;
+
+
+		//before write block data, need wait 1ms (?) (shameless cut and paste :-) gw)
+		init_completion(&host->pause);
+		wait_for_completion_timeout(&host->pause,1);
+		complete(&host->pause);
+
+		ret=-1;
+		i=0;
+
+		//start block token
+		host->write_buffer[0]=(0xfe);
+	
+
+		memcpy(&(host->write_buffer[1]),host->buffer, 512); //?512
+		//crc
+		host->write_buffer[513]=0x0a;
+		host->write_buffer[514]=0xa0;
+
+		for (i=515;i<525;i++)
+		    host->write_buffer[i]=0xff;
+
+		palmtt_xmit(host, 530 );
+
+
+	    cmd->error = MMC_ERR_TIMEOUT;
+
+
+	ret=mmc_palmtt_analyze_response(&(host->read_buffer[515]), 10, 0x05, (char)0x1f);
+	if (ret>-1)
+	{
+		host->data->error = MMC_ERR_NONE;
+		cmd->error = MMC_ERR_NONE;
+		do
+		{
+			for (i=0;i<8;i++)
+			    host->write_buffer[i]=0xff;
+			palmtt_xmit(host, 8 );
+			ret=mmc_palmtt_analyze_response(host->read_buffer, 8, 0, (char)0xff);
+		}
+		while (ret>-1);
+	}
+
+//		host->data->error = MMC_ERR_NONE;
+//		cmd->error = MMC_ERR_NONE;
+
+		init_completion(&host->pause);
+		wait_for_completion_timeout(&host->pause,1);
+		complete(&host->pause);
+
+
+	break;
+
+//-------------ACMD41---------------
+	case SD_APP_OP_COND:
+		ret = mmc_palmtt_command_response(host, cmd->opcode , cmd->arg, 8, 0);
+		
+		if ( ret != -1 )
+		{
+			cmd->resp[0] = mmc_palmtt_read_ocr(host, cmd);
+
+			cmd->error = MMC_ERR_NONE;
+			host->data->error = MMC_ERR_NONE;
+		}
+
+
+cmd->resp[0] = mmc_palmtt_read_ocr(host, cmd);
+	break;
+
+//-------------CMD55---------------
+	case MMC_APP_CMD:
+		ret = mmc_palmtt_command_response(host, cmd->opcode , cmd->arg, 8, 0);
+		
+		if ( ret != -1 )
+		{
+			cmd->error = MMC_ERR_NONE;
+			host->data->error = MMC_ERR_NONE;
+		}
+
+	break;
+
+	    default :
+		printk("--------------- Undefine CMD%d------------------\n",cmd->opcode);
+		host->undef=1;
+		break;
+
+	}
+
+omap_set_gpio_dataout(3,0);
+
+}
+
+static void mmc_palmtt_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+	struct mmc_palmtt_host *host = mmc_priv(mmc);
+	int i;
+
+	WARN_ON(host->mrq != NULL);
+
+	host->mrq = req;
+
+	// Some cards (vendor left unnamed to protect the guilty) seem to
+	// require this delay after power-up. Otherwise we'll get mysterious
+	// data timeouts. 
+
+	if (req->cmd->opcode == MMC_SEND_CSD) {
+		struct mmc_card *card;
+		int broken_present = 0;
+
+		list_for_each_entry(card, &mmc->cards, node) {
+			if (is_broken_card(card)) {
+				broken_present = 1;
+				break;
+			}
+		}
+		if (broken_present) {
+			static int complained = 0;
+
+			if (!complained) {
+				printk(KERN_WARNING "MMC%d: Broken card workaround enabled\n",
+				       host->id);
+				complained = 1;
+			}
+			if (in_interrupt()) {
+				// This is nasty 
+				 printk(KERN_ERR "Sleeping in IRQ handler, FIXME please!\n");
+				 dump_stack();
+				 mdelay(100);
+			} else {
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule_timeout(100 * HZ / 1000);
+			}
+		}
+	}
+
+	//ignore errors
+	for (i=0;i<5;i++) {
+	    
+	    host->bug=0;
+	    host->undef=0;
+	    
+	    mmc_palmtt_prepare_data(host, req);
+	    mmc_palmtt_start_command(host, req->cmd);
+	    
+		if ( ( !host->bug && (host->cmd->error == MMC_ERR_NONE) ) || host->undef )
+		    break;
+	    
+	    DBG("-----------restart command:%d-----------\n", i);
+	}
+
+	mmc_palmtt_cmd_done(host, req->cmd);
+
+}
+
+static void mmc_palmtt_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmc_palmtt_host *host = mmc_priv(mmc);
+	int dsor;
+	int realclock;
+	unsigned int mcbsp_clk=clk_get_rate(host->mcbsp_clk);
+
+
+	if (ios->power_mode == MMC_POWER_UP && ios->clock < 400000) {
+		/* Fix for broken stack */
+		realclock = 400000;
+	} else {
+		realclock = ios->clock;
+	}
+
+	if (ios->clock == 0) {
+		dsor = 1;
+	} else {
+		dsor = mcbsp_clk / realclock;
+		if (dsor < 1)
+			dsor = 1;
+
+		if (mcbsp_clk / dsor > realclock)
+			dsor++;
+
+		if (dsor > 250)
+			dsor = 250;
+	}
+
+	DBG("MMC%d: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n",
+	    host->id, mcbsp_clk / dsor, ios->bus_mode, ios->power_mode,
+	    ios->vdd / 100, ios->vdd % 100);
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		mmc_palmtt_power(host, 0);
+		mmc_palmtt_cs(host, 1);
+		break;
+	case MMC_POWER_ON:
+	case MMC_POWER_UP:
+		mmc_palmtt_power(host, 1);
+		dsor |= 1<<11;
+		break;
+	}
+
+	host->bus_mode = ios->bus_mode;
+
+	host->spi_cfg.clk_div=dsor;
+
+	omap_mcbsp_stop(OMAP_MCBSP2);
+	omap_mcbsp_set_spi_mode(OMAP_MCBSP2, &host->spi_cfg);
+	omap_mcbsp_start(OMAP_MCBSP2);
+
+}
+
+
+static struct mmc_host_ops mmc_palmtt_ops = {
+	.request	= mmc_palmtt_request,
+	.set_ios	= mmc_palmtt_set_ios,
+};
+
+static int mmc_palmtt_probe(struct device *dev)
+{
+	struct mmc_host *mmc;
+	struct mmc_palmtt_host *host = NULL;
+	int ret = 0;
+
+omap_set_gpio_direction(3,0);
+
+	DBG("mmc_palmtt_probe\n");
+	mmc = mmc_alloc_host(sizeof(struct mmc_palmtt_host), dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+
+	host->power_pin = OMAP_GPIO_IRQ(9);
+	host->switch_pin = OMAP_MPUIO(12);
+	host->cs_pin = OMAP_MPUIO(6);
+	host->id = 1;
+
+	host->use_dma=1;
+	host->write_buffer = dma_alloc_coherent(NULL,1024,&host->p_dma_write,
+                                GFP_KERNEL | GFP_DMA);
+	if (!host->write_buffer) {
+		printk(KERN_ERR "MMC: Unable to get dma write buffer\n");
+		goto out;
+	}
+
+	host->read_buffer = dma_alloc_coherent(NULL,1024,&host->p_dma_read,
+                                GFP_KERNEL | GFP_DMA);
+	if (!host->read_buffer) {
+		printk(KERN_ERR "MMC: Unable to get dma read buffer\n");
+		goto out;
+	}
+
+	mmc->ops = &mmc_palmtt_ops;
+	mmc->f_min = 400000;
+	mmc->f_max = 25000000;
+//	mmc->f_max = 10000000;
+	mmc->ocr_avail = MMC_VDD_33_34;
+	host->ocr=0;
+	host->addr=0;
+
+	/* No SG-DMA */
+	mmc->max_phys_segs = 1;
+	mmc->max_seg_size = PAGE_SIZE;
+
+	omap_mcbsp_set_io_type(OMAP_MCBSP2, OMAP_MCBSP_POLL_IO); // OMAP_MCBSP_IRQ_IO
+
+	if ((ret = omap_mcbsp_request(OMAP_MCBSP2)) != 0) {
+		printk(KERN_ERR "MMC%d: Unable to get McBSP2\n",
+		       host->id);
+		goto out;
+	}
+    	host->spi_cfg.spi_mode=OMAP_MCBSP_SPI_MASTER;
+	host->spi_cfg.rx_clock_polarity=OMAP_MCBSP_CLK_FALLING;
+    	host->spi_cfg.tx_clock_polarity=OMAP_MCBSP_CLK_FALLING;
+	host->spi_cfg.fsx_polarity=OMAP_MCBSP_FS_ACTIVE_LOW;
+	host->spi_cfg.clk_stp_mode=OMAP_MCBSP_CLK_STP_MODE_NO_DELAY;
+	host->spi_cfg.clk_div=0x45;
+	host->spi_cfg.word_length=OMAP_MCBSP_WORD_8;
+	host->io_base=io_p2v(OMAP1510_MCBSP2_BASE);
+
+	omap_mcbsp_stop(OMAP_MCBSP2);
+
+	omap_mcbsp_set_spi_mode(OMAP_MCBSP2, &host->spi_cfg);
+
+//	w = OMAP_MCBSP_READ(host->io_base, SPCR2);
+//	OMAP_MCBSP_WRITE(host->io_base, SPCR2, w | 1<<9);
+
+//	w = OMAP_MCBSP_READ(host->io_base, SPCR1);
+//	OMAP_MCBSP_WRITE(host->io_base, SPCR1, w | 1<<15);
+
+	omap_mcbsp_start(OMAP_MCBSP2);
+
+	//hm ;-)
+//	OMAP_MCBSP_WRITE(host->io_base, DXR1,0xff);
+
+	host->mcbsp_clk = clk_get(0, "api_ck");
+	if (IS_ERR(host->mcbsp_clk)) {
+		printk(KERN_ERR "MMC%d: could not acquire api_ck\n",  host->id);
+		omap_mcbsp_free(OMAP_MCBSP2);
+		goto out;
+	}
+	else
+	    printk("MMC%d: clock:%ld\n", host->id, clk_get_rate(host->mcbsp_clk));
+
+	if ((ret = omap_request_gpio(host->power_pin)) != 0) {
+		printk(KERN_ERR "MMC%d: Unable to get GPIO pin for MMC power\n",
+		       host->id);
+		omap_mcbsp_free(OMAP_MCBSP2);
+		goto out;
+	}
+	omap_set_gpio_direction(host->power_pin, 0);
+
+	if ((ret = omap_request_gpio(host->cs_pin)) != 0) {
+		printk(KERN_ERR "MMC%d: Unable to get GPIO pin for MMC CS\n",
+		       host->id);
+		omap_free_gpio(host->power_pin);
+		omap_mcbsp_free(OMAP_MCBSP2);
+		goto out;
+	}
+	omap_set_gpio_direction(host->cs_pin, 0);
+
+	host->dev = dev;
+	dev_set_drvdata(dev, host);
+
+
+	INIT_WORK(&host->switch_work, mmc_palmtt_switch_handler, host);
+	init_timer(&host->switch_timer);
+	host->switch_timer.function = mmc_palmtt_switch_timer;
+	host->switch_timer.data = (unsigned long) host;
+
+	if (omap_request_gpio(host->switch_pin) != 0) {
+		printk(KERN_WARNING "MMC%d: Unable to get GPIO pin for MMC cover switch\n",
+		       host->id);
+		host->switch_pin = -1;
+		omap_free_gpio(host->power_pin);
+		omap_free_gpio(host->cs_pin);
+		omap_mcbsp_free(OMAP_MCBSP2);
+		goto out;
+	}
+
+	omap_set_gpio_direction(host->switch_pin, 1);
+//	omap_set_gpio_edge_ctrl(host->switch_pin, OMAP_GPIO_FALLING_EDGE);
+//	set_irq_type(host->switch_pin, IRQT_FALLING);
+	set_irq_type(host->switch_pin, IRQ_TYPE_EDGE_RISING);
+	ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin),
+				  mmc_palmtt_switch_irq, 0, DRIVER_NAME, host);
+
+	if (ret) {
+	    printk(KERN_WARNING "MMC%d: Unable to get IRQ for MMC cover switch\n",
+	           host->id);
+	    omap_free_gpio(host->power_pin);
+	    omap_free_gpio(host->cs_pin);
+	    omap_free_gpio(host->switch_pin);
+	    omap_mcbsp_free(OMAP_MCBSP2);
+	    host->switch_pin = -1;
+	    goto out;
+	}
+	
+	if (device_create_file(dev, &dev_attr_cover_switch) < 0) {
+	    printk(KERN_WARNING "MMC%d: Unable to create sysfs attribute for cover switch\n", 
+	           host->id);
+	    free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
+	    omap_free_gpio(host->switch_pin);
+	    omap_free_gpio(host->power_pin);
+	    omap_free_gpio(host->cs_pin);
+	    omap_mcbsp_free(OMAP_MCBSP2);
+	    host->switch_pin = -1;
+	    goto out;
+	}
+
+	mmc_add_host(mmc);
+
+	if (mmc_palmtt_cover_is_open(host))
+	    schedule_work(&host->switch_work);
+
+	DBG("mmc_palmtt_probe exit\n");
+
+	return 0;
+out:
+	if (host) {
+
+		if (host->write_buffer)
+		    dma_free_coherent(NULL,1024,(void *) host->write_buffer, host->p_dma_write);
+		if (host->read_buffer)
+		    dma_free_coherent(NULL,1024,(void *) host->read_buffer, host->p_dma_read);
+
+		kfree(host);
+	}
+	return ret;
+}
+
+
+static int mmc_palmtt_remove(struct device *dev)
+{
+	struct mmc_palmtt_host *host = dev_get_drvdata(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+	if (host) {
+
+		del_timer_sync(&host->switch_timer);
+		flush_scheduled_work();
+
+		mmc_remove_host(host->mmc);
+
+		omap_mcbsp_free(OMAP_MCBSP2);
+
+		mmc_palmtt_power(host, 0);
+			
+		omap_free_gpio(host->power_pin);
+		omap_free_gpio(host->cs_pin);
+
+		free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
+		omap_free_gpio(host->switch_pin);
+
+		host->switch_pin = -1;
+
+		if (host->write_buffer)
+		    dma_free_coherent(NULL,1024,(void *) host->write_buffer, host->p_dma_write);
+		if (host->read_buffer)
+		    dma_free_coherent(NULL,1024,(void *) host->read_buffer, host->p_dma_read);
+		kfree(host);
+	}
+
+	return 0;
+}
+
+
+#define mmc_palmtt_suspend	NULL
+#define mmc_palmtt_resume	NULL
+
+static void palmtt_release(struct device *dev)
+{
+	/* Nothing to release? */
+}
+
+static struct platform_device mmc_palmtt_device = {
+	.name		= DRIVER_NAME,
+	.id			= 1,
+	.dev		= {
+		.release = palmtt_release,
+	},
+};
+
+static struct device_driver mmc_palmtt_driver = {
+	.name		= "mmci-palmtt",
+	.bus		= &platform_bus_type,
+	.probe		= mmc_palmtt_probe,
+	.remove		= mmc_palmtt_remove,
+	.suspend	= mmc_palmtt_suspend,
+	.resume		= mmc_palmtt_resume,
+};
+
+
+static int __init mmc_palmtt_init(void)
+{
+	int ret;
+	DBG("mmc_palmtt_init\n");
+
+	ret = driver_register(&mmc_palmtt_driver);
+	if (ret != 0)
+		return -ENODEV;
+
+	ret = platform_device_register(&mmc_palmtt_device);
+	if (ret == 0)
+		return 0;
+
+	    return -ENODEV;
+
+}
+
+static void __exit mmc_palmtt_exit(void)
+{
+	DBG("mmc_palmtt_exit\n");
+	platform_device_unregister(&mmc_palmtt_device);
+	
+	driver_unregister(&mmc_palmtt_driver);
+}
+
+module_init(mmc_palmtt_init);
+module_exit(mmc_palmtt_exit);
+
+MODULE_DESCRIPTION("PalmTT MMC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("marek.vasut@gmail.com");

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2006-10-19 16:28 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-10-19 16:28 [PATCH] MMC support for Palm Tungsten|T Marek Vašut

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox