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ä + * Pin multiplexing and Innovator support by Tony Lindgren + * Changes to make it work on PalmTT2 by Nikolay Petukhov + * Changes to make it work on PalmTT by Marek Vasut + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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;iwrite_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;iwrite_buffer[i]); + + return 0; +} +unsigned char * palmtt_irq_recv(struct mmc_palmtt_host *host, unsigned int length ) { + int i; + + for (i=0;iwrite_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; iwrite_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<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;iwrite_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;iwrite_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; iread_buffer[6+i]); + printk("\n"); + } +#endif + + for (i=0;iread_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;jdata->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;jdata->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;jdata->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;jdata->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");