View | Details | Raw Unified
Collapse All | Expand All

(-) linux-2.6.21-gentoo/drivers/mmc/tifm_sd.c (-779 / +939 lines)
 Lines 17-23    Link Here 
#include <linux/wait.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/workqueue.h>
/* Host registers (relative to pci base address): */
/* Host registers (relative to pci base address): */
enum {
enum {
 Lines 58-71   enum { Link Here 
	SOCK_MS_DATA                   = 0x188,
	SOCK_MS_DATA                   = 0x188,
	SOCK_MS_STATUS                 = 0x18c,
	SOCK_MS_STATUS                 = 0x18c,
	SOCK_MS_SYSTEM                 = 0x190,
	SOCK_MS_SYSTEM                 = 0x190,
	SOCK_FIFO_ACCESS               = 0x200 };
	SOCK_FIFO_ACCESS               = 0x200
};
#define TIFM_IRQ_ENABLE           0x80000000
#define TIFM_IRQ_SOCKMASK(x)      (x)
#define TIFM_IRQ_CARDMASK(x)      ((x) << 8)
#define TIFM_IRQ_FIFOMASK(x)      ((x) << 16)
#define TIFM_IRQ_SETALL           0xffffffff
#define TIFM_CTRL_LED             0x00000040
#define TIFM_CTRL_LED             0x00000040
#define TIFM_CTRL_FAST_CLK        0x00000100
#define TIFM_CTRL_FAST_CLK        0x00000100
 Lines 74-143   enum { Link Here 
#define TIFM_SOCK_STATE_POWERED   0x00000080
#define TIFM_SOCK_STATE_POWERED   0x00000080
#define TIFM_FIFO_ENABLE          0x00000001 /* Meaning of this constant is unverified */
#define TIFM_FIFO_ENABLE          0x00000001 /* Meaning of this constant is unverified */
#define TIFM_FIFO_READY           0x00000001 /* Meaning of this constant is unverified */
#define TIFM_FIFO_INT_SETALL      0x0000ffff
#define TIFM_FIFO_INT_SETALL      0x0000ffff
#define TIFM_FIFO_INTMASK         0x00000005 /* Meaning of this constant is unverified */
#define TIFM_FIFO_INTMASK         0x00000005 /* Meaning of this constant is unverified */
#define TIFM_DMA_RESET            0x00000002 /* Meaning of this constant is unverified */
#define TIFM_DMA_RESET            0x00000002 /* Meaning of this constant is unverified */
#define TIFM_DMA_TX               0x00008000 /* Meaning of this constant is unverified */
#define TIFM_DMA_TX               0x00008000 /* Meaning of this constant is unverified */
#define TIFM_DMA_EN               0x00000001 /* Meaning of this constant is unverified */
#define TIFM_DMA_EN               0x00000001 /* Meaning of this constant is unverified */
#define TIFM_DMA_TSIZE            0x0000007f /* Meaning of this constant is unverified */
typedef enum {FM_NULL = 0, FM_XD = 0x01, FM_MS = 0x02, FM_SD = 0x03} tifm_media_id;
#define TIFM_TYPE_XD 1
#define TIFM_TYPE_MS 2
#define TIFM_TYPE_SD 3
struct tifm_device_id {
	unsigned char type;
};
struct tifm_driver;
struct tifm_driver;
struct tifm_dev {
struct tifm_dev {
	char __iomem            *addr;
	char __iomem  *addr;
	spinlock_t              lock;
	spinlock_t    lock;
	tifm_media_id           media_id;
	unsigned char type;
	unsigned int            socket_id;
	unsigned int  socket_id;
	void                    (*signal_irq)(struct tifm_dev *sock,
	void          (*event)(struct tifm_dev *sock);
					      unsigned int sock_irq_status);
	void          (*data_event)(struct tifm_dev *sock);
	struct tifm_driver      *drv;
	struct device dev;
	struct device           dev;
};
};
struct tifm_driver {
struct tifm_driver {
	tifm_media_id        *id_table;
	struct tifm_device_id *id_table;
	int                  (*probe)(struct tifm_dev *dev);
	int                   (*probe)(struct tifm_dev *dev);
	void                 (*remove)(struct tifm_dev *dev);
	void                  (*remove)(struct tifm_dev *dev);
	int                  (*suspend)(struct tifm_dev *dev,
	int                   (*suspend)(struct tifm_dev *dev,
                                        pm_message_t state);
					 pm_message_t state);
	int                  (*resume)(struct tifm_dev *dev);
	int                   (*resume)(struct tifm_dev *dev);
	struct device_driver driver;
	struct device_driver driver;
};
};
/* Socket caps: */
enum {
	TIFM_SOCK_XD_CAP   = 0x0001,  // socket supports xD cards
	TIFM_SOCK_MS_PIF   = 0x0002   // socket has Memorystick parallel i/f
};
struct tifm_adapter {
struct tifm_adapter {
	char __iomem            *addr;
	char __iomem            *addr;
	spinlock_t              lock;
	spinlock_t              lock;
	unsigned int            irq_status;
	unsigned int            irq_status;
	unsigned int            socket_change_set;
	unsigned int            socket_change_set;
	wait_queue_head_t       change_set_notify;
	unsigned int            id;
	unsigned int            id;
	unsigned int            num_sockets;
	unsigned int            num_sockets;
	struct tifm_dev         **sockets;
	struct completion       *finish_me;
	struct task_struct      *media_switcher;
	struct work_struct      media_switcher;
	struct class_device     cdev;
	struct class_device     cdev;
	struct device           *dev;
	void                    (*eject)(struct tifm_adapter *fm, struct tifm_dev *sock);
	void                    (*eject)(struct tifm_adapter *fm,
					 struct tifm_dev *sock);
	struct socket_t {
		struct tifm_dev *dev;
		unsigned int    caps;
	} sockets[0];
};
};
struct tifm_adapter *tifm_alloc_adapter(void);
struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets,
void tifm_free_device(struct device *dev);
					struct device *dev);
void tifm_free_adapter(struct tifm_adapter *fm);
int tifm_add_adapter(struct tifm_adapter *fm);
int tifm_add_adapter(struct tifm_adapter *fm, int (*mediathreadfn)(void *data));
void tifm_remove_adapter(struct tifm_adapter *fm);
void tifm_remove_adapter(struct tifm_adapter *fm);
struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm);
void tifm_free_adapter(struct tifm_adapter *fm);
void tifm_free_device(struct device *dev);
struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
				   unsigned char type);
int tifm_register_driver(struct tifm_driver *drv);
int tifm_register_driver(struct tifm_driver *drv);
void tifm_unregister_driver(struct tifm_driver *drv);
void tifm_unregister_driver(struct tifm_driver *drv);
void tifm_eject(struct tifm_dev *sock);
void tifm_eject(struct tifm_dev *sock);
unsigned int tifm_get_sock_caps(struct tifm_dev *sock);
int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
		int direction);
		int direction);
void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
		   int direction);
		   int direction);
void tifm_queue_work(struct work_struct *work);
static inline void *tifm_get_drvdata(struct tifm_dev *dev)
static inline void *tifm_get_drvdata(struct tifm_dev *dev)
{
{
 Lines 149-156   static inline void tifm_set_drvdata(stru Link Here 
	dev_set_drvdata(&dev->dev, data);
	dev_set_drvdata(&dev->dev, data);
}
}
struct tifm_device_id {
	tifm_media_id media_id;
};
#endif
#endif
 Lines 9-20    Link Here 
 *
 *
 */
 */
#include <linux/tifm.h>
#include "linux/tifm.h"
#include <linux/dma-mapping.h>
#include "linux/pci_ids.h"
#include <linux/freezer.h>
#define DRIVER_NAME "tifm_7xx1"
#define DRIVER_NAME "tifm_7xx1"
#define DRIVER_VERSION "0.7"
#define DRIVER_VERSION "0.8"
#define TIFM_IRQ_ENABLE           0x80000000
#define TIFM_IRQ_SOCKMASK(x)      (x)
#define TIFM_IRQ_CARDMASK(x)      ((x) << 8)
#define TIFM_IRQ_FIFOMASK(x)      ((x) << 16)
#define TIFM_IRQ_SETALL           0xffffffff
static void tifm_7xx1_dummy_eject(struct tifm_adapter *fm,
				  struct tifm_dev *sock)
{
}
static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
{
{
 Lines 22-28   static void tifm_7xx1_eject(struct tifm_ Link Here 
	spin_lock_irqsave(&fm->lock, flags);
	spin_lock_irqsave(&fm->lock, flags);
	fm->socket_change_set |= 1 << sock->socket_id;
	fm->socket_change_set |= 1 << sock->socket_id;
	wake_up_all(&fm->change_set_notify);
	tifm_queue_work(&fm->media_switcher);
	spin_unlock_irqrestore(&fm->lock, flags);
	spin_unlock_irqrestore(&fm->lock, flags);
}
}
 Lines 30-37   static irqreturn_t tifm_7xx1_isr(int irq Link Here 
{
{
	struct tifm_adapter *fm = dev_id;
	struct tifm_adapter *fm = dev_id;
	struct tifm_dev *sock;
	struct tifm_dev *sock;
	unsigned int irq_status;
	unsigned int irq_status, cnt;
	unsigned int sock_irq_status, cnt;
	spin_lock(&fm->lock);
	spin_lock(&fm->lock);
	irq_status = readl(fm->addr + FM_INTERRUPT_STATUS);
	irq_status = readl(fm->addr + FM_INTERRUPT_STATUS);
 Lines 44-56   static irqreturn_t tifm_7xx1_isr(int irq Link Here 
		writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
		writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
		for (cnt = 0; cnt < fm->num_sockets; cnt++) {
		for (cnt = 0; cnt < fm->num_sockets; cnt++) {
			sock = fm->sockets[cnt];
			sock = fm->sockets[cnt].dev;
			sock_irq_status = (irq_status >> cnt)
			if (sock) {
					  & (TIFM_IRQ_FIFOMASK(1)
				if ((irq_status >> cnt) & TIFM_IRQ_FIFOMASK(1))
					     | TIFM_IRQ_CARDMASK(1));
					sock->data_event(sock);
				if ((irq_status >> cnt) & TIFM_IRQ_CARDMASK(1))
			if (sock && sock_irq_status)
					sock->event(sock);
				sock->signal_irq(sock, sock_irq_status);
			}
		}
		}
		fm->socket_change_set |= irq_status
		fm->socket_change_set |= irq_status
 Lines 58-114   static irqreturn_t tifm_7xx1_isr(int irq Link Here 
	}
	}
	writel(irq_status, fm->addr + FM_INTERRUPT_STATUS);
	writel(irq_status, fm->addr + FM_INTERRUPT_STATUS);
	if (!fm->socket_change_set)
	if (fm->finish_me)
		complete_all(fm->finish_me);
	else if (!fm->socket_change_set)
		writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
		writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
	else
	else
		wake_up_all(&fm->change_set_notify);
		tifm_queue_work(&fm->media_switcher);
	spin_unlock(&fm->lock);
	spin_unlock(&fm->lock);
	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}
static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr,
static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr,
						 int is_x2)
						 unsigned int caps)
{
{
	unsigned int s_state;
	unsigned int s_state;
	int cnt;
	int cnt;
	writel(0x0e00, sock_addr + SOCK_CONTROL);
	writel(0x0e00, sock_addr + SOCK_CONTROL);
	for (cnt = 0; cnt < 100; cnt++) {
	/* half a second should be enough  */
	for (cnt = 16; cnt <= 256; cnt <<= 1) {
		if (!(TIFM_SOCK_STATE_POWERED
		if (!(TIFM_SOCK_STATE_POWERED
		      & readl(sock_addr + SOCK_PRESENT_STATE)))
		      & readl(sock_addr + SOCK_PRESENT_STATE)))
			break;
			break;
		msleep(10);
		msleep(cnt);
	}
	}
	s_state = readl(sock_addr + SOCK_PRESENT_STATE);
	s_state = readl(sock_addr + SOCK_PRESENT_STATE);
	if (!(TIFM_SOCK_STATE_OCCUPIED & s_state))
	if (!(TIFM_SOCK_STATE_OCCUPIED & s_state))
		return FM_NULL;
		return 0;
	if (is_x2) {
	writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED,
		writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL);
	       sock_addr + SOCK_CONTROL);
	} else {
		// SmartMedia cards need extra 40 msec
	if (caps & TIFM_SOCK_XD_CAP) {
		if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1)
		if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1)
			msleep(40);
			msleep(40);
		writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED,
		       sock_addr + SOCK_CONTROL);
		msleep(10);
		writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED,
			sock_addr + SOCK_CONTROL);
	}
	}
	writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL);
	msleep(10);
	for (cnt = 0; cnt < 100; cnt++) {
	for (cnt = 16; cnt <= 256; cnt <<= 1) {
		if ((TIFM_SOCK_STATE_POWERED
		if ((TIFM_SOCK_STATE_POWERED
		     & readl(sock_addr + SOCK_PRESENT_STATE)))
		     & readl(sock_addr + SOCK_PRESENT_STATE)))
			break;
			break;
		msleep(10);
		msleep(cnt);
	}
	}
	if (!is_x2)
	writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED),
		writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED),
	       sock_addr + SOCK_CONTROL);
		       sock_addr + SOCK_CONTROL);
	return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7;
	return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7;
}
}
 Lines 119-245   tifm_7xx1_sock_addr(char __iomem *base_a Link Here 
	return base_addr + ((sock_num + 1) << 10);
	return base_addr + ((sock_num + 1) << 10);
}
}
static int tifm_7xx1_switch_media(void *data)
static void tifm_7xx1_switch_media(struct work_struct *work)
{
{
	struct tifm_adapter *fm = data;
	struct tifm_adapter *fm = container_of(work, struct tifm_adapter,
	unsigned long flags;
					       media_switcher);
	tifm_media_id media_id;
	char *card_name = "xx";
	int cnt, rc;
	struct tifm_dev *sock;
	struct tifm_dev *sock;
	unsigned int socket_change_set;
	unsigned long flags;
	unsigned char media_id;
	while (1) {
	unsigned int socket_change_set, cnt;
		rc = wait_event_interruptible(fm->change_set_notify,
					      fm->socket_change_set);
		if (rc == -ERESTARTSYS)
			try_to_freeze();
		spin_lock_irqsave(&fm->lock, flags);
	spin_lock_irqsave(&fm->lock, flags);
		socket_change_set = fm->socket_change_set;
	socket_change_set = fm->socket_change_set;
		fm->socket_change_set = 0;
	fm->socket_change_set = 0;
		dev_dbg(fm->dev, "checking media set %x\n",
	dev_dbg(fm->cdev.dev, "checking media set %x\n",
			socket_change_set);
		socket_change_set);
		if (kthread_should_stop())
	if (!socket_change_set) {
			socket_change_set = (1 << fm->num_sockets) - 1;
		spin_unlock_irqrestore(&fm->lock, flags);
		spin_unlock_irqrestore(&fm->lock, flags);
		return;
	}
		if (!socket_change_set)
	for (cnt = 0; cnt < fm->num_sockets; cnt++) {
		if (!(socket_change_set & (1 << cnt)))
			continue;
			continue;
		sock = fm->sockets[cnt].dev;
		spin_lock_irqsave(&fm->lock, flags);
		if (sock) {
		for (cnt = 0; cnt < fm->num_sockets; cnt++) {
			printk(KERN_INFO
			if (!(socket_change_set & (1 << cnt)))
			       "%s : demand removing card from socket %u:%u\n",
				continue;
			       fm->cdev.class_id, fm->id, cnt);
			sock = fm->sockets[cnt];
			fm->sockets[cnt].dev = NULL;
			if (sock) {
				printk(KERN_INFO DRIVER_NAME
				       ": demand removing card from socket %d\n",
				       cnt);
				fm->sockets[cnt] = NULL;
				spin_unlock_irqrestore(&fm->lock, flags);
				device_unregister(&sock->dev);
				spin_lock_irqsave(&fm->lock, flags);
				writel(0x0e00,
				       tifm_7xx1_sock_addr(fm->addr, cnt)
				       + SOCK_CONTROL);
			}
			if (kthread_should_stop())
				continue;
			spin_unlock_irqrestore(&fm->lock, flags);
			spin_unlock_irqrestore(&fm->lock, flags);
			media_id = tifm_7xx1_toggle_sock_power(
			device_unregister(&sock->dev);
					tifm_7xx1_sock_addr(fm->addr, cnt),
			spin_lock_irqsave(&fm->lock, flags);
					fm->num_sockets == 2);
			writel(0x0e00, tifm_7xx1_sock_addr(fm->addr, cnt)
			if (media_id) {
			       + SOCK_CONTROL);
				sock = tifm_alloc_device(fm);
				if (sock) {
					sock->addr = tifm_7xx1_sock_addr(fm->addr,
									 cnt);
					sock->media_id = media_id;
					sock->socket_id = cnt;
					switch (media_id) {
					case 1:
						card_name = "xd";
						break;
					case 2:
						card_name = "ms";
						break;
					case 3:
						card_name = "sd";
						break;
					default:
						tifm_free_device(&sock->dev);
						spin_lock_irqsave(&fm->lock, flags);
						continue;
					}
					snprintf(sock->dev.bus_id, BUS_ID_SIZE,
						 "tifm_%s%u:%u", card_name,
						 fm->id, cnt);
					printk(KERN_INFO DRIVER_NAME
					       ": %s card detected in socket %d\n",
					       card_name, cnt);
					if (!device_register(&sock->dev)) {
						spin_lock_irqsave(&fm->lock, flags);
						if (!fm->sockets[cnt]) {
							fm->sockets[cnt] = sock;
							sock = NULL;
						}
						spin_unlock_irqrestore(&fm->lock, flags);
					}
					if (sock)
						tifm_free_device(&sock->dev);
				}
				spin_lock_irqsave(&fm->lock, flags);
			}
		}
		}
		if (!kthread_should_stop()) {
		spin_unlock_irqrestore(&fm->lock, flags);
			writel(TIFM_IRQ_FIFOMASK(socket_change_set)
			       | TIFM_IRQ_CARDMASK(socket_change_set),
		media_id = tifm_7xx1_toggle_sock_power(
			       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
				tifm_7xx1_sock_addr(fm->addr, cnt),
			writel(TIFM_IRQ_FIFOMASK(socket_change_set)
				fm->sockets[cnt].caps);
			       | TIFM_IRQ_CARDMASK(socket_change_set),
			       fm->addr + FM_SET_INTERRUPT_ENABLE);
		// tifm_alloc_device will check if media_id is valid
			writel(TIFM_IRQ_ENABLE,
		sock = tifm_alloc_device(fm, cnt, media_id);
			       fm->addr + FM_SET_INTERRUPT_ENABLE);
		if (sock) {
			spin_unlock_irqrestore(&fm->lock, flags);
			sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt);
		} else {
			for (cnt = 0; cnt < fm->num_sockets; cnt++) {
			if (!device_register(&sock->dev)) {
				if (fm->sockets[cnt])
				spin_lock_irqsave(&fm->lock, flags);
					fm->socket_change_set |= 1 << cnt;
				if (!fm->sockets[cnt].dev) {
			}
					fm->sockets[cnt].dev = sock;
			if (!fm->socket_change_set) {
					sock = NULL;
				spin_unlock_irqrestore(&fm->lock, flags);
				}
				return 0;
			} else {
				spin_unlock_irqrestore(&fm->lock, flags);
				spin_unlock_irqrestore(&fm->lock, flags);
			}
			}
			if (sock)
				tifm_free_device(&sock->dev);
		}
		}
		spin_lock_irqsave(&fm->lock, flags);
	}
	}
	return 0;
	writel(TIFM_IRQ_FIFOMASK(socket_change_set)
	       | TIFM_IRQ_CARDMASK(socket_change_set),
	       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_FIFOMASK(socket_change_set)
	       | TIFM_IRQ_CARDMASK(socket_change_set),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
	spin_unlock_irqrestore(&fm->lock, flags);
}
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM
 Lines 258-266   static int tifm_7xx1_suspend(struct pci_ Link Here 
static int tifm_7xx1_resume(struct pci_dev *dev)
static int tifm_7xx1_resume(struct pci_dev *dev)
{
{
	struct tifm_adapter *fm = pci_get_drvdata(dev);
	struct tifm_adapter *fm = pci_get_drvdata(dev);
	int cnt, rc;
	int rc;
	unsigned int good_sockets = 0, bad_sockets = 0;
	unsigned long flags;
	unsigned long flags;
	tifm_media_id new_ids[fm->num_sockets];
	unsigned char new_ids[fm->num_sockets];
	DECLARE_COMPLETION_ONSTACK(finish_resume);
	pci_set_power_state(dev, PCI_D0);
	pci_set_power_state(dev, PCI_D0);
	pci_restore_state(dev);
	pci_restore_state(dev);
 Lines 271-315   static int tifm_7xx1_resume(struct pci_d Link Here 
	dev_dbg(&dev->dev, "resuming host\n");
	dev_dbg(&dev->dev, "resuming host\n");
	for (cnt = 0; cnt < fm->num_sockets; cnt++)
	for (rc = 0; rc < fm->num_sockets; rc++)
		new_ids[cnt] = tifm_7xx1_toggle_sock_power(
		new_ids[rc] = tifm_7xx1_toggle_sock_power(
					tifm_7xx1_sock_addr(fm->addr, cnt),
					tifm_7xx1_sock_addr(fm->addr, rc),
					fm->num_sockets == 2);
					fm->sockets[rc].caps);
	spin_lock_irqsave(&fm->lock, flags);
	spin_lock_irqsave(&fm->lock, flags);
	fm->socket_change_set = 0;
	for (rc = 0; rc < fm->num_sockets; rc++) {
	for (cnt = 0; cnt < fm->num_sockets; cnt++) {
		if (fm->sockets[rc].dev) {
		if (fm->sockets[cnt]) {
			if (fm->sockets[rc].dev->type == new_ids[rc])
			if (fm->sockets[cnt]->media_id == new_ids[cnt])
				good_sockets |= 1 << rc;
				fm->socket_change_set |= 1 << cnt;
			else
				bad_sockets |= 1 << rc;
			fm->sockets[cnt]->media_id = new_ids[cnt];
		}
		}
	}
	}
	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	if (!fm->socket_change_set) {
	dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n",
		spin_unlock_irqrestore(&fm->lock, flags);
		good_sockets, bad_sockets);
		return 0;
	} else {
	fm->socket_change_set = 0;
		fm->socket_change_set = 0;
	if (good_sockets) {
		fm->finish_me = &finish_resume;
		spin_unlock_irqrestore(&fm->lock, flags);
		spin_unlock_irqrestore(&fm->lock, flags);
		rc = wait_for_completion_timeout(&finish_resume, HZ);
		dev_dbg(&dev->dev, "wait returned %d\n", rc);
		writel(TIFM_IRQ_FIFOMASK(good_sockets)
		       | TIFM_IRQ_CARDMASK(good_sockets),
		       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
		writel(TIFM_IRQ_FIFOMASK(good_sockets)
		       | TIFM_IRQ_CARDMASK(good_sockets),
		       fm->addr + FM_SET_INTERRUPT_ENABLE);
		spin_lock_irqsave(&fm->lock, flags);
		fm->finish_me = NULL;
		fm->socket_change_set ^= good_sockets & fm->socket_change_set;
	}
	}
	wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ);
	fm->socket_change_set |= bad_sockets;
	if (fm->socket_change_set)		
		tifm_queue_work(&fm->media_switcher);
	spin_lock_irqsave(&fm->lock, flags);
	spin_unlock_irqrestore(&fm->lock, flags);
	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
	       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_ENABLE,
	writel(TIFM_IRQ_ENABLE,
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	fm->socket_change_set = 0;
	spin_unlock_irqrestore(&fm->lock, flags);
	return 0;
	return 0;
}
}
 Lines 345-364   static int tifm_7xx1_probe(struct pci_de Link Here 
	pci_intx(dev, 1);
	pci_intx(dev, 1);
	fm = tifm_alloc_adapter();
	fm = tifm_alloc_adapter(dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM
				? 4 : 2, &dev->dev);
	if (!fm) {
	if (!fm) {
		rc = -ENOMEM;
		rc = -ENOMEM;
		goto err_out_int;
		goto err_out_int;
	}
	}
	fm->dev = &dev->dev;
	switch (fm->num_sockets) {
	fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM)
	case 2:
			  ? 4 : 2;
		fm->sockets[0].caps |= TIFM_SOCK_MS_PIF;
	fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets,
		break;
			      GFP_KERNEL);
	case 4:
	if (!fm->sockets)
		fm->sockets[0].caps |= TIFM_SOCK_XD_CAP;
		goto err_out_free;
		fm->sockets[1].caps |= TIFM_SOCK_XD_CAP;
		fm->sockets[2].caps |= TIFM_SOCK_XD_CAP | TIFM_SOCK_MS_PIF;
		fm->sockets[3].caps |= TIFM_SOCK_XD_CAP;
		break;
	}
	INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
	fm->eject = tifm_7xx1_eject;
	fm->eject = tifm_7xx1_eject;
	pci_set_drvdata(dev, fm);
	pci_set_drvdata(dev, fm);
 Lines 367-385   static int tifm_7xx1_probe(struct pci_de Link Here 
	if (!fm->addr)
	if (!fm->addr)
		goto err_out_free;
		goto err_out_free;
	rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm);
	rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm);
	if (rc)
	if (rc)
		goto err_out_unmap;
		goto err_out_unmap;
	init_waitqueue_head(&fm->change_set_notify);
	rc = tifm_add_adapter(fm);
	rc = tifm_add_adapter(fm, tifm_7xx1_switch_media);
	if (rc)
	if (rc)
		goto err_out_irq;
		goto err_out_irq;
	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	wake_up_process(fm->media_switcher);
	return 0;
	return 0;
err_out_irq:
err_out_irq:
 Lines 401-418   err_out: Link Here 
static void tifm_7xx1_remove(struct pci_dev *dev)
static void tifm_7xx1_remove(struct pci_dev *dev)
{
{
	struct tifm_adapter *fm = pci_get_drvdata(dev);
	struct tifm_adapter *fm = pci_get_drvdata(dev);
	unsigned long flags;
	fm->eject = tifm_7xx1_dummy_eject;
	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	mmiowb();
	mmiowb();
	free_irq(dev->irq, fm);
	free_irq(dev->irq, fm);
	spin_lock_irqsave(&fm->lock, flags);
	fm->socket_change_set = (1 << fm->num_sockets) - 1;
	spin_unlock_irqrestore(&fm->lock, flags);
	kthread_stop(fm->media_switcher);
	tifm_remove_adapter(fm);
	tifm_remove_adapter(fm);
	pci_set_drvdata(dev, NULL);
	pci_set_drvdata(dev, NULL);
 Lines 9-84    Link Here 
 *
 *
 */
 */
#include <linux/tifm.h>
#include "linux/tifm.h"
#include <linux/init.h>
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/idr.h>
#define DRIVER_NAME "tifm_core"
#define DRIVER_NAME "tifm_core"
#define DRIVER_VERSION "0.7"
#define DRIVER_VERSION "0.8"
static struct workqueue_struct *workqueue;
static DEFINE_IDR(tifm_adapter_idr);
static DEFINE_IDR(tifm_adapter_idr);
static DEFINE_SPINLOCK(tifm_adapter_lock);
static DEFINE_SPINLOCK(tifm_adapter_lock);
static tifm_media_id *tifm_device_match(tifm_media_id *ids,
static const char *tifm_media_type_name(unsigned char type, unsigned char nt)
			struct tifm_dev *dev)
{
{
	while (*ids) {
	const char *card_type_name[3][3] = {
		if (dev->media_id == *ids)
		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
			return ids;
		{ "XD", "MS", "SD"},
		ids++;
		{ "xd", "ms", "sd"}
	}
	};
	return NULL;
	if (nt > 2 || type < 1 || type > 3)
		return NULL;
	return card_type_name[nt][type - 1];
}
}
static int tifm_match(struct device *dev, struct device_driver *drv)
static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id)
{
{
	struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
	if (sock->type == id->type)
	struct tifm_driver *fm_drv;
	fm_drv = container_of(drv, struct tifm_driver, driver);
	if (!fm_drv->id_table)
		return -EINVAL;
	if (tifm_device_match(fm_drv->id_table, fm_dev))
		return 1;
		return 1;
	return -ENODEV;
	return 0;
}
static int tifm_bus_match(struct device *dev, struct device_driver *drv)
{
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	struct tifm_driver *fm_drv = container_of(drv, struct tifm_driver,
						  driver);
	struct tifm_device_id *ids = fm_drv->id_table;
	if (ids) {
		while (ids->type) {
			if (tifm_dev_match(sock, ids))
				return 1;
			++ids;
		}
	}
	return 0;
}
}
static int tifm_uevent(struct device *dev, char **envp, int num_envp,
static int tifm_uevent(struct device *dev, char **envp, int num_envp,
		       char *buffer, int buffer_size)
		       char *buffer, int buffer_size)
{
{
	struct tifm_dev *fm_dev;
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	int i = 0;
	int i = 0;
	int length = 0;
	int length = 0;
	const char *card_type_name[] = {"INV", "SM", "MS", "SD"};
	if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev)))
		return -ENODEV;
	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
			"TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id]))
			   "TIFM_CARD_TYPE=%s",
			   tifm_media_type_name(sock->type, 1)))
		return -ENOMEM;
		return -ENOMEM;
	return 0;
	return 0;
}
}
static int tifm_device_probe(struct device *dev)
{
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
					       driver);
	int rc = -ENODEV;
	get_device(dev);
	if (dev->driver && drv->probe) {
		rc = drv->probe(sock);
		if (!rc)
			return 0;
	}
	put_device(dev);
	return rc;
}
static void tifm_dummy_event(struct tifm_dev *sock)
{
	return;
}
static int tifm_device_remove(struct device *dev)
{
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
					       driver);
	if (dev->driver && drv->remove) {
		sock->event = tifm_dummy_event;
		sock->data_event = tifm_dummy_event;
		drv->remove(sock);
		sock->dev.driver = NULL;
	}
	put_device(dev);
	return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM
static int tifm_device_suspend(struct device *dev, pm_message_t state)
static int tifm_device_suspend(struct device *dev, pm_message_t state)
{
{
	struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	struct tifm_driver *drv = fm_dev->drv;
	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
					       driver);
	if (drv && drv->suspend)
	if (dev->driver && drv->suspend)
		return drv->suspend(fm_dev, state);
		return drv->suspend(sock, state);
	return 0;
	return 0;
}
}
static int tifm_device_resume(struct device *dev)
static int tifm_device_resume(struct device *dev)
{
{
	struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	struct tifm_driver *drv = fm_dev->drv;
	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
					       driver);
	if (drv && drv->resume)
	if (dev->driver && drv->resume)
		return drv->resume(fm_dev);
		return drv->resume(sock);
	return 0;
	return 0;
}
}
 Lines 89-107   static int tifm_device_resume(struct dev Link Here 
#endif /* CONFIG_PM */
#endif /* CONFIG_PM */
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
			 char *buf)
{
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	return sprintf(buf, "%x", sock->type);
}
static struct device_attribute tifm_dev_attrs[] = {
	__ATTR(type, S_IRUGO, type_show, NULL),
	__ATTR_NULL
};
static struct bus_type tifm_bus_type = {
static struct bus_type tifm_bus_type = {
	.name    = "tifm",
	.name           = "tifm",
	.match   = tifm_match,
	.dev_attrs      = tifm_dev_attrs,
	.uevent  = tifm_uevent,
	.match          = tifm_bus_match,
	.suspend = tifm_device_suspend,
	.uevent         = tifm_uevent,
	.resume  = tifm_device_resume
	.probe          = tifm_device_probe,
	.remove         = tifm_device_remove,
	.suspend        = tifm_device_suspend,
	.resume         = tifm_device_resume
};
};
static void tifm_free(struct class_device *cdev)
static void tifm_free(struct class_device *cdev)
{
{
	struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev);
	struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev);
	kfree(fm->sockets);
	kfree(fm);
	kfree(fm);
}
}
 Lines 110-137   static struct class tifm_adapter_class = Link Here 
	.release = tifm_free
	.release = tifm_free
};
};
struct tifm_adapter *tifm_alloc_adapter(void)
struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets,
					struct device *dev)
{
{
	struct tifm_adapter *fm;
	struct tifm_adapter *fm;
	fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL);
	fm = kzalloc(sizeof(struct tifm_adapter)
		     + sizeof(struct socket_t) * num_sockets, GFP_KERNEL);
	if (fm) {
	if (fm) {
		fm->cdev.class = &tifm_adapter_class;
		fm->cdev.class = &tifm_adapter_class;
		spin_lock_init(&fm->lock);
		fm->cdev.dev = dev;
		class_device_initialize(&fm->cdev);
		class_device_initialize(&fm->cdev);
		spin_lock_init(&fm->lock);
		fm->num_sockets = num_sockets;
	}
	}
	return fm;
	return fm;
}
}
EXPORT_SYMBOL(tifm_alloc_adapter);
EXPORT_SYMBOL(tifm_alloc_adapter);
void tifm_free_adapter(struct tifm_adapter *fm)
int tifm_add_adapter(struct tifm_adapter *fm)
{
	class_device_put(&fm->cdev);
}
EXPORT_SYMBOL(tifm_free_adapter);
int tifm_add_adapter(struct tifm_adapter *fm,
		     int (*mediathreadfn)(void *data))
{
{
	int rc;
	int rc;
 Lines 141-199   int tifm_add_adapter(struct tifm_adapter Link Here 
	spin_lock(&tifm_adapter_lock);
	spin_lock(&tifm_adapter_lock);
	rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id);
	rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id);
	spin_unlock(&tifm_adapter_lock);
	spin_unlock(&tifm_adapter_lock);
	if (!rc) {
	if (rc)
		snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
		return rc;
		fm->media_switcher = kthread_create(mediathreadfn,
						    fm, "tifm/%u", fm->id);
		if (!IS_ERR(fm->media_switcher))
			return class_device_add(&fm->cdev);
	snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
	rc = class_device_add(&fm->cdev);
	if (rc) {
		spin_lock(&tifm_adapter_lock);
		spin_lock(&tifm_adapter_lock);
		idr_remove(&tifm_adapter_idr, fm->id);
		idr_remove(&tifm_adapter_idr, fm->id);
		spin_unlock(&tifm_adapter_lock);
		spin_unlock(&tifm_adapter_lock);
		rc = -ENOMEM;
		return rc;
	}
	}
	return rc;
	return 0;
}
}
EXPORT_SYMBOL(tifm_add_adapter);
EXPORT_SYMBOL(tifm_add_adapter);
void tifm_remove_adapter(struct tifm_adapter *fm)
void tifm_remove_adapter(struct tifm_adapter *fm)
{
{
	class_device_del(&fm->cdev);
	unsigned int cnt;
	flush_workqueue(workqueue);
	for (cnt = 0; cnt < fm->num_sockets; ++cnt) {
		if (fm->sockets[cnt].dev)
			device_unregister(&fm->sockets[cnt].dev->dev);
	}
	spin_lock(&tifm_adapter_lock);
	spin_lock(&tifm_adapter_lock);
	idr_remove(&tifm_adapter_idr, fm->id);
	idr_remove(&tifm_adapter_idr, fm->id);
	spin_unlock(&tifm_adapter_lock);
	spin_unlock(&tifm_adapter_lock);
	class_device_del(&fm->cdev);
}
}
EXPORT_SYMBOL(tifm_remove_adapter);
EXPORT_SYMBOL(tifm_remove_adapter);
void tifm_free_device(struct device *dev)
void tifm_free_adapter(struct tifm_adapter *fm)
{
{
	struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
	class_device_put(&fm->cdev);
	kfree(fm_dev);
}
}
EXPORT_SYMBOL(tifm_free_device);
EXPORT_SYMBOL(tifm_free_adapter);
static void tifm_dummy_signal_irq(struct tifm_dev *sock,
void tifm_free_device(struct device *dev)
				  unsigned int sock_irq_status)
{
{
	return;
	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
	kfree(sock);
}
}
EXPORT_SYMBOL(tifm_free_device);
struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm)
struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
				   unsigned char type)
{
{
	struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL);
	struct tifm_dev *sock = NULL;
	if (dev) {
	if (!tifm_media_type_name(type, 0))
		spin_lock_init(&dev->lock);
		return sock;
		dev->dev.parent = fm->dev;
	sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL);
		dev->dev.bus = &tifm_bus_type;
	if (sock) {
		dev->dev.release = tifm_free_device;
		spin_lock_init(&sock->lock);
		dev->signal_irq = tifm_dummy_signal_irq;
		sock->type = type;
		sock->socket_id = id;
		sock->event = tifm_dummy_event;
		sock->data_event = tifm_dummy_event;
		sock->dev.parent = fm->cdev.dev;
		sock->dev.bus = &tifm_bus_type;
		sock->dev.dma_mask = fm->cdev.dev->dma_mask;
		sock->dev.release = tifm_free_device;
		snprintf(sock->dev.bus_id, BUS_ID_SIZE,
			 "tifm_%s%u:%u", tifm_media_type_name(type, 2),
			 fm->id, id);
		printk(KERN_INFO DRIVER_NAME
		       ": %s card detected in socket %u:%u\n",
		       tifm_media_type_name(type, 0), fm->id, id);
	}
	}
	return dev;
	return sock;
}
}
EXPORT_SYMBOL(tifm_alloc_device);
EXPORT_SYMBOL(tifm_alloc_device);
 Lines 218-271   void tifm_unmap_sg(struct tifm_dev *sock Link Here 
}
}
EXPORT_SYMBOL(tifm_unmap_sg);
EXPORT_SYMBOL(tifm_unmap_sg);
static int tifm_device_probe(struct device *dev)
void tifm_queue_work(struct work_struct *work)
{
{
	struct tifm_driver *drv;
	queue_work(workqueue, work);
	struct tifm_dev *fm_dev;
	int rc = 0;
	const tifm_media_id *id;
	drv = container_of(dev->driver, struct tifm_driver, driver);
	fm_dev = container_of(dev, struct tifm_dev, dev);
	get_device(dev);
	if (!fm_dev->drv && drv->probe && drv->id_table) {
		rc = -ENODEV;
		id = tifm_device_match(drv->id_table, fm_dev);
		if (id)
			rc = drv->probe(fm_dev);
		if (rc >= 0) {
			rc = 0;
			fm_dev->drv = drv;
		}
	}
	if (rc)
		put_device(dev);
	return rc;
}
static int tifm_device_remove(struct device *dev)
{
	struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
	struct tifm_driver *drv = fm_dev->drv;
	if (drv) {
		fm_dev->signal_irq = tifm_dummy_signal_irq;
		if (drv->remove)
			drv->remove(fm_dev);
		fm_dev->drv = NULL;
	}
	put_device(dev);
	return 0;
}
}
EXPORT_SYMBOL(tifm_queue_work);
int tifm_register_driver(struct tifm_driver *drv)
int tifm_register_driver(struct tifm_driver *drv)
{
{
	drv->driver.bus = &tifm_bus_type;
	drv->driver.bus = &tifm_bus_type;
	drv->driver.probe = tifm_device_probe;
	drv->driver.remove = tifm_device_remove;
	drv->driver.suspend = tifm_device_suspend;
	drv->driver.resume = tifm_device_resume;
	return driver_register(&drv->driver);
	return driver_register(&drv->driver);
}
}
 Lines 279-291   EXPORT_SYMBOL(tifm_unregister_driver); Link Here 
static int __init tifm_init(void)
static int __init tifm_init(void)
{
{
	int rc = bus_register(&tifm_bus_type);
	int rc;
	if (!rc) {
	workqueue = create_freezeable_workqueue("tifm");
		rc = class_register(&tifm_adapter_class);
	if (!workqueue)
		if (rc)
		return -ENOMEM;
			bus_unregister(&tifm_bus_type);
	}
	rc = bus_register(&tifm_bus_type);
	if (rc)
		goto err_out_wq;
	rc = class_register(&tifm_adapter_class);
	if (!rc)
		return 0;
	bus_unregister(&tifm_bus_type);
err_out_wq:
	destroy_workqueue(workqueue);
	return rc;
	return rc;
}
}
 Lines 294-299   static void __exit tifm_exit(void) Link Here 
{
{
	class_unregister(&tifm_adapter_class);
	class_unregister(&tifm_adapter_class);
	bus_unregister(&tifm_bus_type);
	bus_unregister(&tifm_bus_type);
	destroy_workqueue(workqueue);
}
}
subsys_initcall(tifm_init);
subsys_initcall(tifm_init);
 Lines 7-23    Link Here 
 * it under the terms of the GNU General Public License version 2 as
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 * published by the Free Software Foundation.
 *
 *
 * Special thanks to Brad Campbell for extensive testing of this driver.
 *
 */
 */
#include <linux/tifm.h>
#include "linux/tifm.h"
#include <linux/mmc/protocol.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/host.h>
#include <linux/mmc/host.h>
#include <linux/highmem.h>
#include <linux/highmem.h>
#include <asm/io.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_sd"
#define DRIVER_NAME "tifm_sd"
#define DRIVER_VERSION "0.7"
#define DRIVER_VERSION "0.8"
static int no_dma = 0;
static int no_dma = 0;
static int fixed_timeout = 0;
static int fixed_timeout = 0;
 Lines 36-44   module_param(fixed_timeout, bool, 0644); Link Here 
#define TIFM_MMCSD_INAB       0x0080   /* abort / initialize command */
#define TIFM_MMCSD_INAB       0x0080   /* abort / initialize command */
#define TIFM_MMCSD_READ       0x8000
#define TIFM_MMCSD_READ       0x8000
#define TIFM_MMCSD_DATAMASK   0x401d   /* set bits: CERR, EOFB, BRS, CB, EOC */
#define TIFM_MMCSD_ERRMASK    0x01e0   /* set bits: CCRC, CTO, DCRC, DTO */
#define TIFM_MMCSD_ERRMASK    0x01e0   /* set bits: CCRC, CTO, DCRC, DTO */
#define TIFM_MMCSD_EOC        0x0001   /* end of command phase  */
#define TIFM_MMCSD_EOC        0x0001   /* end of command phase  */
#define TIFM_MMCSD_CD         0x0002   /* card detect           */
#define TIFM_MMCSD_CB         0x0004   /* card enter busy state */
#define TIFM_MMCSD_CB         0x0004   /* card enter busy state */
#define TIFM_MMCSD_BRS        0x0008   /* block received/sent   */
#define TIFM_MMCSD_BRS        0x0008   /* block received/sent   */
#define TIFM_MMCSD_EOFB       0x0010   /* card exit busy state  */
#define TIFM_MMCSD_EOFB       0x0010   /* card exit busy state  */
 Lines 48-55   module_param(fixed_timeout, bool, 0644); Link Here 
#define TIFM_MMCSD_CCRC       0x0100   /* command crc error     */
#define TIFM_MMCSD_CCRC       0x0100   /* command crc error     */
#define TIFM_MMCSD_AF         0x0400   /* fifo almost full      */
#define TIFM_MMCSD_AF         0x0400   /* fifo almost full      */
#define TIFM_MMCSD_AE         0x0800   /* fifo almost empty     */
#define TIFM_MMCSD_AE         0x0800   /* fifo almost empty     */
#define TIFM_MMCSD_OCRB       0x1000   /* OCR busy              */
#define TIFM_MMCSD_CIRQ       0x2000   /* card irq (cmd40/sdio) */
#define TIFM_MMCSD_CERR       0x4000   /* card status error     */
#define TIFM_MMCSD_CERR       0x4000   /* card status error     */
#define TIFM_MMCSD_ODTO       0x0040   /* open drain / extended timeout */
#define TIFM_MMCSD_CARD_RO    0x0200   /* card is read-only     */
#define TIFM_MMCSD_FIFO_SIZE  0x0020
#define TIFM_MMCSD_FIFO_SIZE  0x0020
#define TIFM_MMCSD_RSP_R0     0x0000
#define TIFM_MMCSD_RSP_R0     0x0000
 Lines 67-163   module_param(fixed_timeout, bool, 0644); Link Here 
#define TIFM_MMCSD_CMD_AC     0x2000
#define TIFM_MMCSD_CMD_AC     0x2000
#define TIFM_MMCSD_CMD_ADTC   0x3000
#define TIFM_MMCSD_CMD_ADTC   0x3000
typedef enum {
#define TIFM_MMCSD_MAX_BLOCK_SIZE  0x0800
	IDLE = 0,
	CMD,    /* main command ended                   */
	BRS,    /* block transfer finished              */
	SCMD,   /* stop command ended                   */
	CARD,   /* card left busy state                 */
	FIFO,   /* FIFO operation completed (uncertain) */
	READY
} card_state_t;
enum {
enum {
	FIFO_RDY   = 0x0001,     /* hardware dependent value */
	CMD_READY    = 0x0001,
	EJECT      = 0x0004,
	FIFO_READY   = 0x0002,
	EJECT_DONE = 0x0008,
	BRS_READY    = 0x0004,
	CARD_BUSY  = 0x0010,
	SCMD_ACTIVE  = 0x0008,
	OPENDRAIN  = 0x0040,     /* hardware dependent value */
	SCMD_READY   = 0x0010,
	CARD_EVENT = 0x0100,     /* hardware dependent value */
	CARD_BUSY    = 0x0020,
	CARD_RO    = 0x0200,     /* hardware dependent value */
	DATA_CARRY   = 0x0040
	FIFO_EVENT = 0x10000 };  /* hardware dependent value */
};
struct tifm_sd {
struct tifm_sd {
	struct tifm_dev     *dev;
	struct tifm_dev       *dev;
	unsigned int        flags;
	unsigned short        eject:1,
	card_state_t        state;
			      open_drain:1,
	unsigned int        clk_freq;
			      no_dma:1;
	unsigned int        clk_div;
	unsigned short        cmd_flags;
	unsigned long       timeout_jiffies;
	unsigned int          clk_freq;
	unsigned int          clk_div;
	unsigned long         timeout_jiffies;
	struct tasklet_struct finish_tasklet;
	struct tasklet_struct finish_tasklet;
	struct timer_list     timer;
	struct timer_list     timer;
	struct mmc_request    *req;
	struct mmc_request    *req;
	wait_queue_head_t     notify;
	size_t                written_blocks;
	size_t                buffer_size;
	size_t                buffer_pos;
	int                   sg_len;
	int                   sg_pos;
	unsigned int          block_pos;
	struct scatterlist    bounce_buf;
	unsigned char         bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE];
};
};
static char* tifm_sd_data_buffer(struct mmc_data *data)
/* for some reason, host won't respond correctly to readw/writew */
static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
			      unsigned int off, unsigned int cnt)
{
{
	return page_address(data->sg->page) + data->sg->offset;
	struct tifm_dev *sock = host->dev;
	unsigned char *buf;
	unsigned int pos = 0, val;
	buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off;
	if (host->cmd_flags & DATA_CARRY) {
		buf[pos++] = host->bounce_buf_data[0];
		host->cmd_flags &= ~DATA_CARRY;
	}
	while (pos < cnt) {
		val = readl(sock->addr + SOCK_MMCSD_DATA);
		buf[pos++] = val & 0xff;
		if (pos == cnt) {
			host->bounce_buf_data[0] = (val >> 8) & 0xff;
			host->cmd_flags |= DATA_CARRY;
			break;
		}
		buf[pos++] = (val >> 8) & 0xff;
	}
	kunmap_atomic(buf - off, KM_BIO_DST_IRQ);
}
}
static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host,
static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
				 unsigned int host_status)
			       unsigned int off, unsigned int cnt)
{
{
	struct mmc_command *cmd = host->req->cmd;
	struct tifm_dev *sock = host->dev;
	unsigned int t_val = 0, cnt = 0;
	unsigned char *buf;
	char *buffer;
	unsigned int pos = 0, val;
	if (host_status & TIFM_MMCSD_BRS) {
	buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off;
		/* in non-dma rx mode BRS fires when fifo is still not empty */
	if (host->cmd_flags & DATA_CARRY) {
		if (no_dma && (cmd->data->flags & MMC_DATA_READ)) {
		val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00);
			buffer = tifm_sd_data_buffer(host->req->data);
		writel(val, sock->addr + SOCK_MMCSD_DATA);
			while (host->buffer_size > host->buffer_pos) {
		host->cmd_flags &= ~DATA_CARRY;
				t_val = readl(sock->addr + SOCK_MMCSD_DATA);
	}
				buffer[host->buffer_pos++] = t_val & 0xff;
				buffer[host->buffer_pos++] =
	while (pos < cnt) {
							(t_val >> 8) & 0xff;
		val = buf[pos++];
			}
		if (pos == cnt) {
			host->bounce_buf_data[0] = val & 0xff;
			host->cmd_flags |= DATA_CARRY;
			break;
		}
		}
		return 1;
		val |= (buf[pos++] << 8) & 0xff00;
	} else if (no_dma) {
		writel(val, sock->addr + SOCK_MMCSD_DATA);
		buffer = tifm_sd_data_buffer(host->req->data);
	}
		if ((cmd->data->flags & MMC_DATA_READ) &&
	kunmap_atomic(buf - off, KM_BIO_SRC_IRQ);
				(host_status & TIFM_MMCSD_AF)) {
}
			for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) {
				t_val = readl(sock->addr + SOCK_MMCSD_DATA);
static void tifm_sd_transfer_data(struct tifm_sd *host)
				if (host->buffer_size > host->buffer_pos) {
{
					buffer[host->buffer_pos++] =
	struct mmc_data *r_data = host->req->cmd->data;
							t_val & 0xff;
	struct scatterlist *sg = r_data->sg;
					buffer[host->buffer_pos++] =
	unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2;
							(t_val >> 8) & 0xff;
	unsigned int p_off, p_cnt;
				}
	struct page *pg;
			}
		} else if ((cmd->data->flags & MMC_DATA_WRITE)
	if (host->sg_pos == host->sg_len)
			   && (host_status & TIFM_MMCSD_AE)) {
		return;
			for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) {
	while (t_size) {
				if (host->buffer_size > host->buffer_pos) {
		cnt = sg[host->sg_pos].length - host->block_pos;
					t_val = buffer[host->buffer_pos++]
		if (!cnt) {
						& 0x00ff;
			host->block_pos = 0;
					t_val |= ((buffer[host->buffer_pos++])
			host->sg_pos++;
						  << 8) & 0xff00;
			if (host->sg_pos == host->sg_len) {
					writel(t_val,
				if ((r_data->flags & MMC_DATA_WRITE)
					       sock->addr + SOCK_MMCSD_DATA);
				    && DATA_CARRY)
				}
					writel(host->bounce_buf_data[0],
					       host->dev->addr
					       + SOCK_MMCSD_DATA);
				return;
			}
			}
			cnt = sg[host->sg_pos].length;
		}
		off = sg[host->sg_pos].offset + host->block_pos;
		pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT);
		p_off = offset_in_page(off);
		p_cnt = PAGE_SIZE - p_off;
		p_cnt = min(p_cnt, cnt);
		p_cnt = min(p_cnt, t_size);
		if (r_data->flags & MMC_DATA_READ)
			tifm_sd_read_fifo(host, pg, p_off, p_cnt);
		else if (r_data->flags & MMC_DATA_WRITE)
			tifm_sd_write_fifo(host, pg, p_off, p_cnt);
		t_size -= p_cnt;
		host->block_pos += p_cnt;
	}
}
static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off,
			      struct page *src, unsigned int src_off,
			      unsigned int count)
{
	unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off;
	unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off;
	memcpy(dst_buf, src_buf, count);
	kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ);
	kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ);
}
static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
{
	struct scatterlist *sg = r_data->sg;
	unsigned int t_size = r_data->blksz;
	unsigned int off, cnt;
	unsigned int p_off, p_cnt;
	struct page *pg;
	dev_dbg(&host->dev->dev, "bouncing block\n");
	while (t_size) {
		cnt = sg[host->sg_pos].length - host->block_pos;
		if (!cnt) {
			host->block_pos = 0;
			host->sg_pos++;
			if (host->sg_pos == host->sg_len)
				return;
			cnt = sg[host->sg_pos].length;
		}
		}
		off = sg[host->sg_pos].offset + host->block_pos;
		pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT);
		p_off = offset_in_page(off);
		p_cnt = PAGE_SIZE - p_off;
		p_cnt = min(p_cnt, cnt);
		p_cnt = min(p_cnt, t_size);
		if (r_data->flags & MMC_DATA_WRITE)
			tifm_sd_copy_page(host->bounce_buf.page,
					  r_data->blksz - t_size,
					  pg, p_off, p_cnt);
		else if (r_data->flags & MMC_DATA_READ)
			tifm_sd_copy_page(pg, p_off, host->bounce_buf.page,
					  r_data->blksz - t_size, p_cnt);
		t_size -= p_cnt;
		host->block_pos += p_cnt;
	}
}
int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data)
{
	struct tifm_dev *sock = host->dev;
	unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz;
	unsigned int dma_len, dma_blk_cnt, dma_off;
	struct scatterlist *sg = NULL;
	unsigned long flags;
	if (host->sg_pos == host->sg_len)
		return 1;
	if (host->cmd_flags & DATA_CARRY) {
		host->cmd_flags &= ~DATA_CARRY;
		local_irq_save(flags);
		tifm_sd_bounce_block(host, r_data);
		local_irq_restore(flags);
		if (host->sg_pos == host->sg_len)
			return 1;
	}
	while (1) {
		dma_len = sg_dma_len(&r_data->sg[host->sg_pos])
			  - host->block_pos;
		if (dma_len)
			break;
		host->block_pos = 0;
		host->sg_pos++;
		if (host->sg_pos == host->sg_len)
			return 1;
	}
	if (dma_len < t_size) {
		dma_blk_cnt = dma_len / r_data->blksz;
		dma_off = host->block_pos;
		host->block_pos += dma_blk_cnt * r_data->blksz;
	} else {
		dma_blk_cnt = TIFM_DMA_TSIZE;
		dma_off = host->block_pos;
		host->block_pos += t_size;
	}
	}
	if (dma_blk_cnt)
		sg = &r_data->sg[host->sg_pos];
	else if (dma_len) {
		if (r_data->flags & MMC_DATA_WRITE) {
			local_irq_save(flags);
			tifm_sd_bounce_block(host, r_data);
			local_irq_restore(flags);
		} else
			host->cmd_flags |= DATA_CARRY;
		sg = &host->bounce_buf;
		dma_off = 0;
		dma_blk_cnt = 1;
	} else
		return 1;
	dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt);
	writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS);
	if (r_data->flags & MMC_DATA_WRITE)
		writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
		       sock->addr + SOCK_DMA_CONTROL);
	else
		writel((dma_blk_cnt << 8) | TIFM_DMA_EN,
		       sock->addr + SOCK_DMA_CONTROL);
	return 0;
	return 0;
}
}
 Lines 207-213   static void tifm_sd_exec(struct tifm_sd Link Here 
{
{
	struct tifm_dev *sock = host->dev;
	struct tifm_dev *sock = host->dev;
	unsigned int cmd_mask = tifm_sd_op_flags(cmd) |
	unsigned int cmd_mask = tifm_sd_op_flags(cmd) |
				(host->flags & OPENDRAIN);
				(host->open_drain ? TIFM_MMCSD_ODTO : 0);
	if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
	if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
		cmd_mask |= TIFM_MMCSD_READ;
		cmd_mask |= TIFM_MMCSD_READ;
 Lines 232-422   static void tifm_sd_fetch_resp(struct mm Link Here 
		       | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00);
		       | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00);
}
}
static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host,
static void tifm_sd_check_status(struct tifm_sd *host)
				       unsigned int host_status)
{
{
	struct tifm_dev *sock = host->dev;
	struct mmc_command *cmd = host->req->cmd;
	struct mmc_command *cmd = host->req->cmd;
change_state:
	if (cmd->error != MMC_ERR_NONE)
	switch (host->state) {
		goto finish_request;
	case IDLE:
	if (!(host->cmd_flags & CMD_READY))
		return;
		return;
	case CMD:
		if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) {
	if (cmd->data) {
			tifm_sd_fetch_resp(cmd, sock);
		if (cmd->data->error != MMC_ERR_NONE) {
			if (cmd->data) {
			if ((host->cmd_flags & SCMD_ACTIVE)
				host->state = BRS;
			    && !(host->cmd_flags & SCMD_READY))
			} else {
				return;
				host->state = READY;
			}
			goto finish_request;
			goto change_state;
		}
		break;
	case BRS:
		if (tifm_sd_transfer_data(sock, host, host_status)) {
			if (cmd->data->flags & MMC_DATA_WRITE) {
				host->state = CARD;
			} else {
				if (no_dma) {
					if (host->req->stop) {
						tifm_sd_exec(host, host->req->stop);
						host->state = SCMD;
					} else {
						host->state = READY;
					}
				} else {
					host->state = FIFO;
				}
			}
			goto change_state;
		}
		break;
	case SCMD:
		if (host_status & TIFM_MMCSD_EOC) {
			tifm_sd_fetch_resp(host->req->stop, sock);
			host->state = READY;
			goto change_state;
		}
		}
		break;
	case CARD:
		if (!(host->cmd_flags & BRS_READY))
		dev_dbg(&sock->dev, "waiting for CARD, have %zd blocks\n",
			return;
			host->written_blocks);
		if (!(host->flags & CARD_BUSY)
		if (!(host->no_dma || (host->cmd_flags & FIFO_READY)))
		    && (host->written_blocks == cmd->data->blocks)) {
			return;
			if (no_dma) {
				if (host->req->stop) {
		if (cmd->data->flags & MMC_DATA_WRITE) {
			if (host->req->stop) {
				if (!(host->cmd_flags & SCMD_ACTIVE)) {
					host->cmd_flags |= SCMD_ACTIVE;
					writel(TIFM_MMCSD_EOFB
					       | readl(sock->addr
						       + SOCK_MMCSD_INT_ENABLE),
					       sock->addr
					       + SOCK_MMCSD_INT_ENABLE);
					tifm_sd_exec(host, host->req->stop);
					tifm_sd_exec(host, host->req->stop);
					host->state = SCMD;
					return;
				} else {
				} else {
					host->state = READY;
					if (!(host->cmd_flags & SCMD_READY)
					    || (host->cmd_flags & CARD_BUSY))
						return;
					writel((~TIFM_MMCSD_EOFB)
					       & readl(sock->addr
						       + SOCK_MMCSD_INT_ENABLE),
					       sock->addr
					       + SOCK_MMCSD_INT_ENABLE);
				}
				}
			} else {
			} else {
				host->state = FIFO;
				if (host->cmd_flags & CARD_BUSY)
					return;
				writel((~TIFM_MMCSD_EOFB)
				       & readl(sock->addr
					       + SOCK_MMCSD_INT_ENABLE),
				       sock->addr + SOCK_MMCSD_INT_ENABLE);
			}
			}
			goto change_state;
		} else {
		}
		break;
	case FIFO:
		if (host->flags & FIFO_RDY) {
			host->flags &= ~FIFO_RDY;
			if (host->req->stop) {
			if (host->req->stop) {
				tifm_sd_exec(host, host->req->stop);
				if (!(host->cmd_flags & SCMD_ACTIVE)) {
				host->state = SCMD;
					host->cmd_flags |= SCMD_ACTIVE;
			} else {
					tifm_sd_exec(host, host->req->stop);
				host->state = READY;
					return;
				} else {
					if (!(host->cmd_flags & SCMD_READY))
						return;
				}
			}
			}
			goto change_state;
		}
		}
		break;
	case READY:
		tasklet_schedule(&host->finish_tasklet);
		return;
	}
	}
finish_request:
	tasklet_schedule(&host->finish_tasklet);
}
}
/* Called from interrupt handler */
/* Called from interrupt handler */
static void tifm_sd_signal_irq(struct tifm_dev *sock,
static void tifm_sd_data_event(struct tifm_dev *sock)
			       unsigned int sock_irq_status)
{
{
	struct tifm_sd *host;
	struct tifm_sd *host;
	unsigned int host_status = 0, fifo_status = 0;
	unsigned int fifo_status = 0;
	int error_code = 0;
	struct mmc_data *r_data = NULL;
	spin_lock(&sock->lock);
	spin_lock(&sock->lock);
	host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
	host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
	fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
	dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
		fifo_status, host->cmd_flags);
	if (sock_irq_status & FIFO_EVENT) {
	if (host->req) {
		fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
		r_data = host->req->cmd->data;
		writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
		host->flags |= fifo_status & FIFO_RDY;
		if (r_data && (fifo_status & TIFM_FIFO_READY)) {
			if (tifm_sd_set_dma_data(host, r_data)) {
				host->cmd_flags |= FIFO_READY;
				tifm_sd_check_status(host);
			}
		}
	}
	}
	if (sock_irq_status & CARD_EVENT) {
	writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
		host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
	spin_unlock(&sock->lock);
		writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
}
		if (!host->req)
/* Called from interrupt handler */
			goto done;
static void tifm_sd_event(struct tifm_dev *sock)
{
	struct tifm_sd *host;
	unsigned int host_status = 0;
	int cmd_error = MMC_ERR_NONE;
	struct mmc_command *cmd = NULL;
	unsigned long flags;
	spin_lock(&sock->lock);
	host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
	host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
	dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
		host_status, host->cmd_flags);
	if (host->req) {
		cmd = host->req->cmd;
		if (host_status & TIFM_MMCSD_ERRMASK) {
		if (host_status & TIFM_MMCSD_ERRMASK) {
			if (host_status & (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO))
			writel(host_status & TIFM_MMCSD_ERRMASK,
				error_code = MMC_ERR_TIMEOUT;
			       sock->addr + SOCK_MMCSD_STATUS);
			else if (host_status
			if (host_status & TIFM_MMCSD_CTO)
				 & (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC))
				cmd_error = MMC_ERR_TIMEOUT;
				error_code = MMC_ERR_BADCRC;
			else if (host_status & TIFM_MMCSD_CCRC)
				cmd_error = MMC_ERR_BADCRC;
			if (cmd->data) {
				if (host_status & TIFM_MMCSD_DTO)
					cmd->data->error = MMC_ERR_TIMEOUT;
				else if (host_status & TIFM_MMCSD_DCRC)
					cmd->data->error = MMC_ERR_BADCRC;
			}
			writel(TIFM_FIFO_INT_SETALL,
			writel(TIFM_FIFO_INT_SETALL,
			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
			writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
			writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
			if (host->req->stop) {
			if (host->req->stop) {
				if (host->state == SCMD) {
				if (host->cmd_flags & SCMD_ACTIVE) {
					host->req->stop->error = error_code;
					host->req->stop->error = cmd_error;
				} else if (host->state == BRS
					host->cmd_flags |= SCMD_READY;
					   || host->state == CARD
				} else {
					   || host->state == FIFO) {
					cmd->error = cmd_error;
					host->req->cmd->error = error_code;
					host->cmd_flags |= SCMD_ACTIVE;
					tifm_sd_exec(host, host->req->stop);
					tifm_sd_exec(host, host->req->stop);
					host->state = SCMD;
					goto done;
					goto done;
				} else {
					host->req->cmd->error = error_code;
				}
				}
			} else {
			} else
				host->req->cmd->error = error_code;
				cmd->error = cmd_error;
		} else {
			if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) {
				if (!(host->cmd_flags & CMD_READY)) {
					host->cmd_flags |= CMD_READY;
					tifm_sd_fetch_resp(cmd, sock);
				} else if (host->cmd_flags & SCMD_ACTIVE) {
					host->cmd_flags |= SCMD_READY;
					tifm_sd_fetch_resp(host->req->stop,
							   sock);
				}
			}
			}
			host->state = READY;
			if (host_status & TIFM_MMCSD_BRS)
				host->cmd_flags |= BRS_READY;
		}
		}
		if (host_status & TIFM_MMCSD_CB)
		if (host->no_dma && cmd->data) {
			host->flags |= CARD_BUSY;
			if (host_status & TIFM_MMCSD_AE)
		if ((host_status & TIFM_MMCSD_EOFB)
				writel(host_status & TIFM_MMCSD_AE,
		    && (host->flags & CARD_BUSY)) {
				       sock->addr + SOCK_MMCSD_STATUS);
			host->written_blocks++;
			host->flags &= ~CARD_BUSY;
			if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF
					   | TIFM_MMCSD_BRS)) {
				local_irq_save(flags);
				tifm_sd_transfer_data(host);
				local_irq_restore(flags);
				host_status &= ~TIFM_MMCSD_AE;
			}
		}
		}
        }
	if (host->req)
		tifm_sd_process_cmd(sock, host, host_status);
done:
	dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n",
		host_status, fifo_status);
	spin_unlock(&sock->lock);
}
static void tifm_sd_prepare_data(struct tifm_sd *host, struct mmc_command *cmd)
{
	struct tifm_dev *sock = host->dev;
	unsigned int dest_cnt;
	/* DMA style IO */
	dev_dbg(&sock->dev, "setting dma for %d blocks\n",
		cmd->data->blocks);
	writel(TIFM_FIFO_INT_SETALL,
	       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
	writel(ilog2(cmd->data->blksz) - 2,
	       sock->addr + SOCK_FIFO_PAGE_SIZE);
	writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL);
	writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
	dest_cnt = (cmd->data->blocks) << 8;
	writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS);
		if (host_status & TIFM_MMCSD_EOFB)
			host->cmd_flags &= ~CARD_BUSY;
		else if (host_status & TIFM_MMCSD_CB)
			host->cmd_flags |= CARD_BUSY;
	writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS);
		tifm_sd_check_status(host);
	writel(cmd->data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN);
	if (cmd->data->flags & MMC_DATA_WRITE) {
		writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
		writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN,
		       sock->addr + SOCK_DMA_CONTROL);
	} else {
		writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
		writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL);
	}
	}
done:
	writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
	spin_unlock(&sock->lock);
}
}
static void tifm_sd_set_data_timeout(struct tifm_sd *host,
static void tifm_sd_set_data_timeout(struct tifm_sd *host,
 Lines 452-597   static void tifm_sd_request(struct mmc_h Link Here 
	struct tifm_sd *host = mmc_priv(mmc);
	struct tifm_sd *host = mmc_priv(mmc);
	struct tifm_dev *sock = host->dev;
	struct tifm_dev *sock = host->dev;
	unsigned long flags;
	unsigned long flags;
	int sg_count = 0;
	struct mmc_data *r_data = mrq->cmd->data;
	struct mmc_data *r_data = mrq->cmd->data;
	spin_lock_irqsave(&sock->lock, flags);
	spin_lock_irqsave(&sock->lock, flags);
	if (host->flags & EJECT) {
	if (host->eject) {
		spin_unlock_irqrestore(&sock->lock, flags);
		spin_unlock_irqrestore(&sock->lock, flags);
		goto err_out;
		goto err_out;
	}
	}
	if (host->req) {
	if (host->req) {
		printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n");
		printk(KERN_ERR "%s : unfinished request detected\n",
		       sock->dev.bus_id);
		spin_unlock_irqrestore(&sock->lock, flags);
		spin_unlock_irqrestore(&sock->lock, flags);
		goto err_out;
		goto err_out;
	}
	}
	host->cmd_flags = 0;
	host->block_pos = 0;
	host->sg_pos = 0;
	if (r_data) {
	if (r_data) {
		tifm_sd_set_data_timeout(host, r_data);
		tifm_sd_set_data_timeout(host, r_data);
		sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len,
		if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop)
				       mrq->cmd->flags & MMC_DATA_WRITE
			 writel(TIFM_MMCSD_EOFB
				       ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
				| readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
		if (sg_count != 1) {
				sock->addr + SOCK_MMCSD_INT_ENABLE);
			printk(KERN_ERR DRIVER_NAME
				": scatterlist map failed\n");
		if (host->no_dma) {
			spin_unlock_irqrestore(&sock->lock, flags);
			writel(TIFM_MMCSD_BUFINT
			goto err_out;
			       | readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
		}
			       sock->addr + SOCK_MMCSD_INT_ENABLE);
			writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8)
		host->written_blocks = 0;
			       | (TIFM_MMCSD_FIFO_SIZE - 1),
		host->flags &= ~CARD_BUSY;
			       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
		tifm_sd_prepare_data(host, mrq->cmd);
	}
	host->req = mrq;
	mod_timer(&host->timer, jiffies + host->timeout_jiffies);
	host->state = CMD;
	writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
	       sock->addr + SOCK_CONTROL);
	tifm_sd_exec(host, mrq->cmd);
	spin_unlock_irqrestore(&sock->lock, flags);
	return;
err_out:
	if (sg_count > 0)
		tifm_unmap_sg(sock, r_data->sg, r_data->sg_len,
			      (r_data->flags & MMC_DATA_WRITE)
			      ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
	mrq->cmd->error = MMC_ERR_TIMEOUT;
	mmc_request_done(mmc, mrq);
}
static void tifm_sd_end_cmd(unsigned long data)
{
	struct tifm_sd *host = (struct tifm_sd*)data;
	struct tifm_dev *sock = host->dev;
	struct mmc_host *mmc = tifm_get_drvdata(sock);
	struct mmc_request *mrq;
	struct mmc_data *r_data = NULL;
	unsigned long flags;
	spin_lock_irqsave(&sock->lock, flags);
	del_timer(&host->timer);
	mrq = host->req;
	host->req = NULL;
	host->state = IDLE;
	if (!mrq) {
		printk(KERN_ERR DRIVER_NAME ": no request to complete?\n");
		spin_unlock_irqrestore(&sock->lock, flags);
		return;
	}
	r_data = mrq->cmd->data;
			host->sg_len = r_data->sg_len;
	if (r_data) {
		if (r_data->flags & MMC_DATA_WRITE) {
			r_data->bytes_xfered = host->written_blocks
					       * r_data->blksz;
		} else {
		} else {
			r_data->bytes_xfered = r_data->blocks -
			host->bounce_buf.offset
				readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1;
				= offset_in_page(host->bounce_buf_data);
			r_data->bytes_xfered *= r_data->blksz;
			host->bounce_buf.length = r_data->blksz;
			r_data->bytes_xfered += r_data->blksz -
				readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1;
			if(1 != tifm_map_sg(sock, &host->bounce_buf, 1,
		}
					    r_data->flags & MMC_DATA_WRITE
		tifm_unmap_sg(sock, r_data->sg, r_data->sg_len,
					    ? PCI_DMA_TODEVICE
			      (r_data->flags & MMC_DATA_WRITE)
					    : PCI_DMA_FROMDEVICE)) {
			      ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
				printk(KERN_ERR "%s : scatterlist map failed\n",
	}
				       sock->dev.bus_id);
				spin_unlock_irqrestore(&sock->lock, flags);
	writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
				goto err_out;
	       sock->addr + SOCK_CONTROL);
			}
			host->sg_len = tifm_map_sg(sock, r_data->sg,
	spin_unlock_irqrestore(&sock->lock, flags);
						   r_data->sg_len,
	mmc_request_done(mmc, mrq);
						   r_data->flags
}
						   & MMC_DATA_WRITE
						   ? PCI_DMA_TODEVICE
static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq)
						   : PCI_DMA_FROMDEVICE);
{
			if (host->sg_len < 1) {
	struct tifm_sd *host = mmc_priv(mmc);
				printk(KERN_ERR "%s : scatterlist map failed\n",
	struct tifm_dev *sock = host->dev;
				       sock->dev.bus_id);
	unsigned long flags;
				tifm_unmap_sg(sock, &host->bounce_buf, 1,
	struct mmc_data *r_data = mrq->cmd->data;
					      r_data->flags & MMC_DATA_WRITE
					      ? PCI_DMA_TODEVICE
	spin_lock_irqsave(&sock->lock, flags);
					      : PCI_DMA_FROMDEVICE);
	if (host->flags & EJECT) {
				spin_unlock_irqrestore(&sock->lock, flags);
		spin_unlock_irqrestore(&sock->lock, flags);
				goto err_out;
		goto err_out;
			}
	}
	if (host->req) {
		printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n");
		spin_unlock_irqrestore(&sock->lock, flags);
		goto err_out;
	}
	if (r_data) {
			writel(TIFM_FIFO_INT_SETALL,
		tifm_sd_set_data_timeout(host, r_data);
			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
			writel(ilog2(r_data->blksz) - 2,
			       sock->addr + SOCK_FIFO_PAGE_SIZE);
			writel(TIFM_FIFO_ENABLE,
			       sock->addr + SOCK_FIFO_CONTROL);
			writel(TIFM_FIFO_INTMASK,
			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
			if (r_data->flags & MMC_DATA_WRITE)
				writel(TIFM_MMCSD_TXDE,
				       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
			else
				writel(TIFM_MMCSD_RXDE,
				       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
		host->buffer_size = mrq->cmd->data->blocks
			tifm_sd_set_dma_data(host, r_data);
				    * mrq->cmd->data->blksz;
		}
		writel(TIFM_MMCSD_BUFINT
		writel(r_data->blocks - 1,
		       | readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
		       sock->addr + SOCK_MMCSD_NUM_BLOCKS);
		       sock->addr + SOCK_MMCSD_INT_ENABLE);
		writel(r_data->blksz - 1,
		writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8)
		       sock->addr + SOCK_MMCSD_BLOCK_LEN);
		       | (TIFM_MMCSD_FIFO_SIZE - 1),
		       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
		host->written_blocks = 0;
		host->flags &= ~CARD_BUSY;
		host->buffer_pos = 0;
		writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS);
		writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN);
	}
	}
	host->req = mrq;
	host->req = mrq;
	mod_timer(&host->timer, jiffies + host->timeout_jiffies);
	mod_timer(&host->timer, jiffies + host->timeout_jiffies);
	host->state = CMD;
	writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
	writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
	       sock->addr + SOCK_CONTROL);
	       sock->addr + SOCK_CONTROL);
	tifm_sd_exec(host, mrq->cmd);
	tifm_sd_exec(host, mrq->cmd);
 Lines 603-609   err_out: Link Here 
	mmc_request_done(mmc, mrq);
	mmc_request_done(mmc, mrq);
}
}
static void tifm_sd_end_cmd_nodma(unsigned long data)
static void tifm_sd_end_cmd(unsigned long data)
{
{
	struct tifm_sd *host = (struct tifm_sd*)data;
	struct tifm_sd *host = (struct tifm_sd*)data;
	struct tifm_dev *sock = host->dev;
	struct tifm_dev *sock = host->dev;
 Lines 617-684   static void tifm_sd_end_cmd_nodma(unsign Link Here 
	del_timer(&host->timer);
	del_timer(&host->timer);
	mrq = host->req;
	mrq = host->req;
	host->req = NULL;
	host->req = NULL;
	host->state = IDLE;
	if (!mrq) {
	if (!mrq) {
		printk(KERN_ERR DRIVER_NAME ": no request to complete?\n");
		printk(KERN_ERR " %s : no request to complete?\n",
		       sock->dev.bus_id);
		spin_unlock_irqrestore(&sock->lock, flags);
		spin_unlock_irqrestore(&sock->lock, flags);
		return;
		return;
	}
	}
	r_data = mrq->cmd->data;
	r_data = mrq->cmd->data;
	if (r_data) {
	if (r_data) {
		writel((~TIFM_MMCSD_BUFINT) &
		if (host->no_dma) {
			readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
			writel((~TIFM_MMCSD_BUFINT)
			sock->addr + SOCK_MMCSD_INT_ENABLE);
			       & readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
			       sock->addr + SOCK_MMCSD_INT_ENABLE);
		if (r_data->flags & MMC_DATA_WRITE) {
			r_data->bytes_xfered = host->written_blocks
					       * r_data->blksz;
		} else {
		} else {
			r_data->bytes_xfered = r_data->blocks -
			tifm_unmap_sg(sock, &host->bounce_buf, 1,
				readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1;
				      (r_data->flags & MMC_DATA_WRITE)
			r_data->bytes_xfered *= r_data->blksz;
				      ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
			r_data->bytes_xfered += r_data->blksz -
			tifm_unmap_sg(sock, r_data->sg, r_data->sg_len,
				readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1;
				      (r_data->flags & MMC_DATA_WRITE)
				      ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
		}
		}
		host->buffer_pos = 0;
		host->buffer_size = 0;
		r_data->bytes_xfered = r_data->blocks
			- readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1;
		r_data->bytes_xfered *= r_data->blksz;
		r_data->bytes_xfered += r_data->blksz
			- readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1;
	}
	}
	writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
	writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
	       sock->addr + SOCK_CONTROL);
	       sock->addr + SOCK_CONTROL);
	spin_unlock_irqrestore(&sock->lock, flags);
	spin_unlock_irqrestore(&sock->lock, flags);
	mmc_request_done(mmc, mrq);
	mmc_request_done(mmc, mrq);
}
}
static void tifm_sd_terminate(struct tifm_sd *host)
{
	struct tifm_dev *sock = host->dev;
	unsigned long flags;
	writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
	mmiowb();
	spin_lock_irqsave(&sock->lock, flags);
	host->flags |= EJECT;
	if (host->req) {
		writel(TIFM_FIFO_INT_SETALL,
		       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
		writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
		tasklet_schedule(&host->finish_tasklet);
	}
	spin_unlock_irqrestore(&sock->lock, flags);
}
static void tifm_sd_abort(unsigned long data)
static void tifm_sd_abort(unsigned long data)
{
{
	struct tifm_sd *host = (struct tifm_sd*)data;
	struct tifm_sd *host = (struct tifm_sd*)data;
	printk(KERN_ERR DRIVER_NAME
	printk(KERN_ERR
	       ": card failed to respond for a long period of time");
	       "%s : card failed to respond for a long period of time "
	       "(%x, %x)\n",
	       host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags);
	tifm_sd_terminate(host);
	tifm_eject(host->dev);
	tifm_eject(host->dev);
}
}
 Lines 691-698   static void tifm_sd_ios(struct mmc_host Link Here 
	spin_lock_irqsave(&sock->lock, flags);
	spin_lock_irqsave(&sock->lock, flags);
	dev_dbg(&sock->dev, "Setting bus width %d, power %d\n", ios->bus_width,
	dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, "
		ios->power_mode);
		"chip_select = %x, power_mode = %x, bus_width = %x\n",
		ios->clock, ios->vdd, ios->bus_mode, ios->chip_select,
		ios->power_mode, ios->bus_width);
	if (ios->bus_width == MMC_BUS_WIDTH_4) {
	if (ios->bus_width == MMC_BUS_WIDTH_4) {
		writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG),
		writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG),
		       sock->addr + SOCK_MMCSD_CONFIG);
		       sock->addr + SOCK_MMCSD_CONFIG);
 Lines 737-777   static void tifm_sd_ios(struct mmc_host Link Here 
		  & readl(sock->addr + SOCK_MMCSD_CONFIG)),
		  & readl(sock->addr + SOCK_MMCSD_CONFIG)),
	       sock->addr + SOCK_MMCSD_CONFIG);
	       sock->addr + SOCK_MMCSD_CONFIG);
	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
	host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN);
		host->flags |= OPENDRAIN;
	else
		host->flags &= ~OPENDRAIN;
	/* chip_select : maybe later */
	/* chip_select : maybe later */
	//vdd
	//vdd
	//power is set before probe / after remove
	//power is set before probe / after remove
	//I believe, power_off when already marked for eject is sufficient to
	// allow removal.
	if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) {
		host->flags |= EJECT_DONE;
		wake_up_all(&host->notify);
	}
	spin_unlock_irqrestore(&sock->lock, flags);
	spin_unlock_irqrestore(&sock->lock, flags);
}
}
static int tifm_sd_ro(struct mmc_host *mmc)
static int tifm_sd_ro(struct mmc_host *mmc)
{
{
	int rc;
	int rc = 0;
	struct tifm_sd *host = mmc_priv(mmc);
	struct tifm_sd *host = mmc_priv(mmc);
	struct tifm_dev *sock = host->dev;
	struct tifm_dev *sock = host->dev;
	unsigned long flags;
	unsigned long flags;
	spin_lock_irqsave(&sock->lock, flags);
	spin_lock_irqsave(&sock->lock, flags);
	if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE))
	host->flags |= (CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE));
		rc = 1;
	rc = (host->flags & CARD_RO) ? 1 : 0;
	spin_unlock_irqrestore(&sock->lock, flags);
	spin_unlock_irqrestore(&sock->lock, flags);
	return rc;
	return rc;
}
}
static struct mmc_host_ops tifm_sd_ops = {
static const struct mmc_host_ops tifm_sd_ops = {
	.request = tifm_sd_request,
	.request = tifm_sd_request,
	.set_ios = tifm_sd_ios,
	.set_ios = tifm_sd_ios,
	.get_ro  = tifm_sd_ro
	.get_ro  = tifm_sd_ro
 Lines 792-798   static int tifm_sd_initialize_host(struc Link Here 
	       sock->addr + SOCK_MMCSD_CONFIG);
	       sock->addr + SOCK_MMCSD_CONFIG);
	/* wait up to 0.51 sec for reset */
	/* wait up to 0.51 sec for reset */
	for (rc = 2; rc <= 256; rc <<= 1) {
	for (rc = 32; rc <= 256; rc <<= 1) {
		if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) {
		if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) {
			rc = 0;
			rc = 0;
			break;
			break;
 Lines 801-808   static int tifm_sd_initialize_host(struc Link Here 
	}
	}
	if (rc) {
	if (rc) {
		printk(KERN_ERR DRIVER_NAME
		printk(KERN_ERR "%s : controller failed to reset\n",
		       ": controller failed to reset\n");
		       sock->dev.bus_id);
		return -ENODEV;
		return -ENODEV;
	}
	}
 Lines 815-822   static int tifm_sd_initialize_host(struc Link Here 
	writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO);
	writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO);
	writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND);
	writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND);
	/* INAB should take much less than reset */
	for (rc = 16; rc <= 64; rc <<= 1) {
	for (rc = 1; rc <= 16; rc <<= 1) {
		host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
		host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
		writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
		writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
		if (!(host_status & TIFM_MMCSD_ERRMASK)
		if (!(host_status & TIFM_MMCSD_ERRMASK)
 Lines 828-839   static int tifm_sd_initialize_host(struc Link Here 
	}
	}
	if (rc) {
	if (rc) {
		printk(KERN_ERR DRIVER_NAME
		printk(KERN_ERR
		       ": card not ready - probe failed on initialization\n");
		       "%s : card not ready - probe failed on initialization\n",
		       sock->dev.bus_id);
		return -ENODEV;
		return -ENODEV;
	}
	}
	writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK,
	writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC
	       | TIFM_MMCSD_ERRMASK,
	       sock->addr + SOCK_MMCSD_INT_ENABLE);
	       sock->addr + SOCK_MMCSD_INT_ENABLE);
	mmiowb();
	mmiowb();
 Lines 848-854   static int tifm_sd_probe(struct tifm_dev Link Here 
	if (!(TIFM_SOCK_STATE_OCCUPIED
	if (!(TIFM_SOCK_STATE_OCCUPIED
	      & readl(sock->addr + SOCK_PRESENT_STATE))) {
	      & readl(sock->addr + SOCK_PRESENT_STATE))) {
		printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n");
		printk(KERN_WARNING "%s : card gone, unexpectedly\n",
		       sock->dev.bus_id);
		return rc;
		return rc;
	}
	}
 Lines 857-897   static int tifm_sd_probe(struct tifm_dev Link Here 
		return -ENOMEM;
		return -ENOMEM;
	host = mmc_priv(mmc);
	host = mmc_priv(mmc);
	host->no_dma = no_dma;
	tifm_set_drvdata(sock, mmc);
	tifm_set_drvdata(sock, mmc);
	host->dev = sock;
	host->dev = sock;
	host->timeout_jiffies = msecs_to_jiffies(1000);
	host->timeout_jiffies = msecs_to_jiffies(1000);
	init_waitqueue_head(&host->notify);
	host->bounce_buf.page = virt_to_page(host->bounce_buf_data);
	tasklet_init(&host->finish_tasklet,
		     no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd,
	tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
		     (unsigned long)host);
		     (unsigned long)host);
	setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host);
	setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host);
	tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request;
	mmc->ops = &tifm_sd_ops;
	mmc->ops = &tifm_sd_ops;
	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
	mmc->f_min = 20000000 / 60;
	mmc->f_min = 20000000 / 60;
	mmc->f_max = 24000000;
	mmc->f_max = 24000000;
	mmc->max_hw_segs = 1;
	mmc->max_phys_segs = 1;
//	mmc->max_blk_count = 2048;
	// limited by DMA counter - it's safer to stick with
//	mmc->max_hw_segs = mmc->max_blk_count;
	// block counter has 11 bits though
	mmc->max_hw_segs = 16;
	mmc->max_blk_count = 256;
//	mmc->max_blk_size = min(2048, PAGE_SIZE);
	// 2k maximum hw block length
//	mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
	mmc->max_blk_size = 2048;
	mmc->max_seg_size = 127 * 2048;
	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
	mmc->max_seg_size = mmc->max_req_size;
	mmc->max_phys_segs = mmc->max_hw_segs;
	sock->signal_irq = tifm_sd_signal_irq;
	mmc->max_sectors = 127 * mmc->max_phys_segs;
//	mmc->max_req_size = mmc->max_seg_size;
	sock->event = tifm_sd_event;
	sock->data_event = tifm_sd_data_event;
	rc = tifm_sd_initialize_host(host);
	rc = tifm_sd_initialize_host(host);
	if (!rc)
	if (!rc)
		rc = mmc_add_host(mmc);
		rc = mmc_add_host(mmc);
	if (rc)
	if (!rc)
		goto out_free_mmc;
		return 0;
	return 0;
out_free_mmc:
	mmc_free_host(mmc);
	mmc_free_host(mmc);
	return rc;
	return rc;
}
}
 Lines 900-918   static void tifm_sd_remove(struct tifm_d Link Here 
{
{
	struct mmc_host *mmc = tifm_get_drvdata(sock);
	struct mmc_host *mmc = tifm_get_drvdata(sock);
	struct tifm_sd *host = mmc_priv(mmc);
	struct tifm_sd *host = mmc_priv(mmc);
	unsigned long flags;
	del_timer_sync(&host->timer);
	tifm_sd_terminate(host);
	wait_event_timeout(host->notify, host->flags & EJECT_DONE,
			   host->timeout_jiffies);
	tasklet_kill(&host->finish_tasklet);
	tasklet_kill(&host->finish_tasklet);
	spin_lock_irqsave(&sock->lock, flags);
	host->eject = 1;
	writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
	mmiowb();
	if (host->req) {
		writel(TIFM_FIFO_INT_SETALL,
		       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
		writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
		host->req->cmd->error = MMC_ERR_TIMEOUT;
		if (host->req->stop)
			host->req->stop->error = MMC_ERR_TIMEOUT;
		tasklet_schedule(&host->finish_tasklet);
	}
	spin_unlock_irqrestore(&sock->lock, flags);
	// temporary hack
	msleep(1000);
	mmc_remove_host(mmc);
	mmc_remove_host(mmc);
	dev_dbg(&sock->dev, "after remove\n");
	/* The meaning of the bit majority in this constant is unknown. */
	/* The meaning of the bit majority in this constant is unknown. */
	writel(0xfff8 & readl(sock->addr + SOCK_CONTROL),
	writel(0xfff8 & readl(sock->addr + SOCK_CONTROL),
	       sock->addr + SOCK_CONTROL);
	       sock->addr + SOCK_CONTROL);
	tifm_set_drvdata(sock, NULL);
	mmc_free_host(mmc);
	mmc_free_host(mmc);
}
}
 Lines 934-947   static int tifm_sd_resume(struct tifm_de Link Here 
{
{
	struct mmc_host *mmc = tifm_get_drvdata(sock);
	struct mmc_host *mmc = tifm_get_drvdata(sock);
	struct tifm_sd *host = mmc_priv(mmc);
	struct tifm_sd *host = mmc_priv(mmc);
	int rc;
	if (sock->media_id != FM_SD
	rc = tifm_sd_initialize_host(host);
	    || tifm_sd_initialize_host(host)) {
	dev_dbg(&sock->dev, "resume initialize %d\n", rc);
		tifm_eject(sock);
		return 0;
	if (rc)
	} else {
		host->eject = 1;
		return mmc_resume_host(mmc);
	else
	}
		rc = mmc_resume_host(mmc);
	return rc;
}
}
#else
#else
 Lines 951-958   static int tifm_sd_resume(struct tifm_de Link Here 
#endif /* CONFIG_PM */
#endif /* CONFIG_PM */
static tifm_media_id tifm_sd_id_tbl[] = {
static struct tifm_device_id tifm_sd_id_tbl[] = {
	FM_SD, 0
	{ TIFM_TYPE_SD }, { }
};
};
static struct tifm_driver tifm_sd_driver = {
static struct tifm_driver tifm_sd_driver = {