All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Marek Vašut" <marek.vasut@gmail.com>
To: linux-omap-open-source@linux.omap.com
Subject: [PATCH] MMC support for Palm Tungsten|T
Date: Thu, 19 Oct 2006 18:28:26 +0200	[thread overview]
Message-ID: <200610191828.26833.marek.vasut@gmail.com> (raw)

[-- 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 --]



                 reply	other threads:[~2006-10-19 16:28 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200610191828.26833.marek.vasut@gmail.com \
    --to=marek.vasut@gmail.com \
    --cc=linux-omap-open-source@linux.omap.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.