Commit 62ea6d80 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (46 commits)
  mmc-omap: Clean up omap set_ios and make MMC_POWER_ON work
  mmc-omap: Fix omap to use MMC_POWER_ON
  mmc-omap: add missing '\n'
  mmc: make tifm_sd_set_dma_data() static
  mmc: remove old card states
  mmc: support unsafe resume of cards
  mmc: separate out reading EXT_CSD
  mmc: break apart switch function
  MMC: Fix handling of low-voltage cards
  MMC: Consolidate voltage definitions
  mmc: add bus handler
  wbsd: check for data opcode earlier
  mmc: Separate out protocol ops
  mmc: Move core functions to subdir
  mmc: deprecate mmc bus topology
  mmc: remove card upon suspend
  mmc: allow suspended block driver to be removed
  mmc: Flush pending detects on host removal
  mmc: Move host and card drivers to subdirs
  mmc: Move queue functions to mmc_block
  ...
parents fa24aa56 d3af5abe
...@@ -11,10 +11,20 @@ ...@@ -11,10 +11,20 @@
#include <linux/tifm.h> #include <linux/tifm.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.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)
{ {
...@@ -22,7 +32,7 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) ...@@ -22,7 +32,7 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
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);
} }
...@@ -30,8 +40,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) ...@@ -30,8 +40,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
{ {
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);
...@@ -45,12 +54,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) ...@@ -45,12 +54,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
for (cnt = 0; cnt < fm->num_sockets; cnt++) { for (cnt = 0; cnt < fm->num_sockets; cnt++) {
sock = fm->sockets[cnt]; sock = fm->sockets[cnt];
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->card_event(sock);
sock->signal_irq(sock, sock_irq_status); }
} }
fm->socket_change_set |= irq_status fm->socket_change_set |= irq_status
...@@ -58,57 +67,57 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) ...@@ -58,57 +67,57 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
} }
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 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++) { 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((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL);
} else {
// SmartMedia cards need extra 40 msec
if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1)
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);
}
for (cnt = 0; cnt < 100; cnt++) { writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED,
sock_addr + SOCK_CONTROL);
/* xd needs some extra time before power on */
if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7)
== TIFM_TYPE_XD)
msleep(40);
writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL);
/* wait for power to stabilize */
msleep(20);
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;
} }
...@@ -119,127 +128,77 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) ...@@ -119,127 +128,77 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num)
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];
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] = 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)
| TIFM_IRQ_CARDMASK(socket_change_set), // tifm_alloc_device will check if media_id is valid
fm->addr + FM_SET_INTERRUPT_ENABLE); sock = tifm_alloc_device(fm, cnt, media_id);
writel(TIFM_IRQ_ENABLE, if (sock) {
fm->addr + FM_SET_INTERRUPT_ENABLE); sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt);
spin_unlock_irqrestore(&fm->lock, flags);
} else { if (!device_register(&sock->dev)) {
for (cnt = 0; cnt < fm->num_sockets; cnt++) { spin_lock_irqsave(&fm->lock, flags);
if (fm->sockets[cnt]) if (!fm->sockets[cnt]) {
fm->socket_change_set |= 1 << cnt; fm->sockets[cnt] = sock;
} sock = NULL;
if (!fm->socket_change_set) { }
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
...@@ -258,9 +217,11 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) ...@@ -258,9 +217,11 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state)
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);
...@@ -271,45 +232,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev) ...@@ -271,45 +232,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
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);
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]) {
if (fm->sockets[cnt]) { if (fm->sockets[rc]->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;
} }
...@@ -345,20 +310,14 @@ static int tifm_7xx1_probe(struct pci_dev *dev, ...@@ -345,20 +310,14 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
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; INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM)
? 4 : 2;
fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets,
GFP_KERNEL);
if (!fm->sockets)
goto err_out_free;
fm->eject = tifm_7xx1_eject; fm->eject = tifm_7xx1_eject;
pci_set_drvdata(dev, fm); pci_set_drvdata(dev, fm);
...@@ -367,19 +326,16 @@ static int tifm_7xx1_probe(struct pci_dev *dev, ...@@ -367,19 +326,16 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
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:
...@@ -401,18 +357,12 @@ static int tifm_7xx1_probe(struct pci_dev *dev, ...@@ -401,18 +357,12 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
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);
......
...@@ -14,71 +14,124 @@ ...@@ -14,71 +14,124 @@
#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->card_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;
} }
...@@ -89,19 +142,33 @@ static int tifm_device_resume(struct device *dev) ...@@ -89,19 +142,33 @@ static int tifm_device_resume(struct device *dev)
#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);
} }
...@@ -110,28 +177,25 @@ static struct class tifm_adapter_class = { ...@@ -110,28 +177,25 @@ static struct class tifm_adapter_class = {
.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 tifm_dev*) * 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;
...@@ -141,59 +205,80 @@ int tifm_add_adapter(struct tifm_adapter *fm, ...@@ -141,59 +205,80 @@ int tifm_add_adapter(struct tifm_adapter *fm,
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;
} }
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])
device_unregister(&fm->sockets[cnt]->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->card_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);
...@@ -218,54 +303,15 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, ...@@ -218,54 +303,15 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
} }
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;
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); queue_work(workqueue, work);
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);
} }
...@@ -279,13 +325,25 @@ EXPORT_SYMBOL(tifm_unregister_driver); ...@@ -279,13 +325,25 @@ EXPORT_SYMBOL(tifm_unregister_driver);
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;
} }
...@@ -294,6 +352,7 @@ static void __exit tifm_exit(void) ...@@ -294,6 +352,7 @@ static void __exit tifm_exit(void)
{ {
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);
......
...@@ -19,110 +19,10 @@ config MMC_DEBUG ...@@ -19,110 +19,10 @@ config MMC_DEBUG
This is an option for use by developers; most people should This is an option for use by developers; most people should
say N here. This enables MMC core and driver debugging. say N here. This enables MMC core and driver debugging.
config MMC_BLOCK source "drivers/mmc/core/Kconfig"
tristate "MMC block device driver"
depends on MMC && BLOCK
default y
help
Say Y here to enable the MMC block device driver support.
This provides a block device driver, which you can use to
mount the filesystem. Almost everyone wishing MMC support
should say Y or M here.
config MMC_ARMMMCI
tristate "ARM AMBA Multimedia Card Interface support"
depends on ARM_AMBA && MMC
help
This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
Interface (PL180 and PL181) support. If you have an ARM(R)
platform with a Multimedia Card slot, say Y or M here.
If unsure, say N.
config MMC_PXA
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
depends on ARCH_PXA && MMC
help
This selects the Intel(R) PXA(R) Multimedia card Interface.
If you have a PXA(R) platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_SDHCI
tristate "Secure Digital Host Controller Interface support (EXPERIMENTAL)"
depends on PCI && MMC && EXPERIMENTAL
help
This select the generic Secure Digital Host Controller Interface.
It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
and Toshiba(R). Most controllers found in laptops are of this type.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP && MMC
select TPS65010 if MACH_OMAP_H2
help
This selects the TI OMAP Multimedia card Interface.
If you have an OMAP board with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_WBSD source "drivers/mmc/card/Kconfig"
tristate "Winbond W83L51xD SD/MMC Card Interface support"
depends on MMC && ISA_DMA_API
help
This selects the Winbond(R) W83L51xD Secure digital and
Multimedia card Interface.
If you have a machine with a integrated W83L518D or W83L519D
SD/MMC card reader, say Y or M here.
If unsure, say N.
config MMC_AU1X
tristate "Alchemy AU1XX0 MMC Card Interface support"
depends on MMC && SOC_AU1200
help
This selects the AMD Alchemy(R) Multimedia card interface.
If you have a Alchemy platform with a MMC slot, say Y or M here.
If unsure, say N.
config MMC_AT91
tristate "AT91 SD/MMC Card Interface support"
depends on ARCH_AT91 && MMC
help
This selects the AT91 MCI controller.
If unsure, say N.
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
depends on ARCH_IMX && MMC
help
This selects the Motorola i.MX Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_TIFM_SD
tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)"
depends on MMC && EXPERIMENTAL && PCI
select TIFM_CORE
help
Say Y here if you want to be able to access MMC/SD cards with
the Texas Instruments(R) Flash Media card reader, found in many
laptops.
This option 'selects' (turns on, enables) 'TIFM_CORE', but you
probably also need appropriate card reader host adapter, such as
'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
(TIFM_7XX1)'.
To compile this driver as a module, choose M here: the source "drivers/mmc/host/Kconfig"
module will be called tifm_sd.
endmenu endmenu
...@@ -2,32 +2,11 @@ ...@@ -2,32 +2,11 @@
# Makefile for the kernel mmc device drivers. # Makefile for the kernel mmc device drivers.
# #
#
# Core
#
obj-$(CONFIG_MMC) += mmc_core.o
#
# Media drivers
#
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
#
# Host drivers
#
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
mmc_core-y := mmc.o mmc_sysfs.o
mmc_core-$(CONFIG_BLOCK) += mmc_queue.o
ifeq ($(CONFIG_MMC_DEBUG),y) ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
endif endif
obj-$(CONFIG_MMC) += core/
obj-$(CONFIG_MMC) += card/
obj-$(CONFIG_MMC) += host/
#
# MMC/SD card drivers
#
comment "MMC/SD Card Drivers"
depends MMC
config MMC_BLOCK
tristate "MMC block device driver"
depends on MMC && BLOCK
default y
help
Say Y here to enable the MMC block device driver support.
This provides a block device driver, which you can use to
mount the filesystem. Almost everyone wishing MMC support
should say Y or M here.
#
# Makefile for MMC/SD card drivers
#
ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
mmc_block-objs := block.o queue.o
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Block driver for media (i.e., flash cards) * Block driver for media (i.e., flash cards)
* *
* Copyright 2002 Hewlett-Packard Company * Copyright 2002 Hewlett-Packard Company
* Copyright 2005-2007 Pierre Ossman
* *
* Use consistent with the GNU GPL is permitted, * Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is * provided that this copyright notice is
...@@ -31,13 +32,13 @@ ...@@ -31,13 +32,13 @@
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/host.h> #include <linux/mmc/sd.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "mmc_queue.h" #include "queue.h"
/* /*
* max 8 partitions per card * max 8 partitions per card
...@@ -223,10 +224,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -223,10 +224,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_data *md = mq->data; struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card; struct mmc_card *card = md->queue.card;
struct mmc_blk_request brq; struct mmc_blk_request brq;
int ret = 1; int ret = 1, sg_pos, data_size;
if (mmc_card_claim_host(card)) mmc_claim_host(card->host);
goto flush_queue;
do { do {
struct mmc_command cmd; struct mmc_command cmd;
...@@ -283,6 +283,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -283,6 +283,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.data.sg = mq->sg; brq.data.sg = mq->sg;
brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
if (brq.data.blocks !=
(req->nr_sectors >> (md->block_bits - 9))) {
data_size = brq.data.blocks * brq.data.blksz;
for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) {
data_size -= mq->sg[sg_pos].length;
if (data_size <= 0) {
mq->sg[sg_pos].length += data_size;
sg_pos++;
break;
}
}
brq.data.sg_len = sg_pos;
}
mmc_wait_for_req(card->host, &brq.mrq); mmc_wait_for_req(card->host, &brq.mrq);
if (brq.cmd.error) { if (brq.cmd.error) {
printk(KERN_ERR "%s: error %d sending read/write command\n", printk(KERN_ERR "%s: error %d sending read/write command\n",
...@@ -342,7 +356,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -342,7 +356,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
spin_unlock_irq(&md->lock); spin_unlock_irq(&md->lock);
} while (ret); } while (ret);
mmc_card_release_host(card); mmc_release_host(card->host);
return 1; return 1;
...@@ -378,9 +392,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -378,9 +392,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
spin_unlock_irq(&md->lock); spin_unlock_irq(&md->lock);
} }
flush_queue: mmc_release_host(card->host);
mmc_card_release_host(card);
spin_lock_irq(&md->lock); spin_lock_irq(&md->lock);
while (ret) { while (ret) {
...@@ -477,11 +489,20 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) ...@@ -477,11 +489,20 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
/* if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
* The CSD capacity field is in units of read_blkbits. /*
* set_capacity takes units of 512 bytes. * The EXT_CSD sector count is in number or 512 byte
*/ * sectors.
set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9)); */
set_capacity(md->disk, card->ext_csd.sectors);
} else {
/*
* The CSD capacity field is in units of read_blkbits.
* set_capacity takes units of 512 bytes.
*/
set_capacity(md->disk,
card->csd.capacity << (card->csd.read_blkbits - 9));
}
return md; return md;
err_putdisk: err_putdisk:
...@@ -502,12 +523,12 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) ...@@ -502,12 +523,12 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
if (mmc_card_blockaddr(card)) if (mmc_card_blockaddr(card))
return 0; return 0;
mmc_card_claim_host(card); mmc_claim_host(card->host);
cmd.opcode = MMC_SET_BLOCKLEN; cmd.opcode = MMC_SET_BLOCKLEN;
cmd.arg = 1 << md->block_bits; cmd.arg = 1 << md->block_bits;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 5); err = mmc_wait_for_cmd(card->host, &cmd, 5);
mmc_card_release_host(card); mmc_release_host(card->host);
if (err) { if (err) {
printk(KERN_ERR "%s: unable to set block size to %d: %d\n", printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
......
/* /*
* linux/drivers/mmc/mmc_queue.c * linux/drivers/mmc/queue.c
* *
* Copyright (C) 2003 Russell King, All Rights Reserved. * Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright 2006-2007 Pierre Ossman
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -14,7 +15,7 @@ ...@@ -14,7 +15,7 @@
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "mmc_queue.h" #include "queue.h"
#define MMC_QUEUE_SUSPENDED (1 << 0) #define MMC_QUEUE_SUSPENDED (1 << 0)
...@@ -179,7 +180,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock ...@@ -179,7 +180,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
blk_cleanup_queue(mq->queue); blk_cleanup_queue(mq->queue);
return ret; return ret;
} }
EXPORT_SYMBOL(mmc_init_queue);
void mmc_cleanup_queue(struct mmc_queue *mq) void mmc_cleanup_queue(struct mmc_queue *mq)
{ {
...@@ -191,6 +191,9 @@ void mmc_cleanup_queue(struct mmc_queue *mq) ...@@ -191,6 +191,9 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
q->queuedata = NULL; q->queuedata = NULL;
spin_unlock_irqrestore(q->queue_lock, flags); spin_unlock_irqrestore(q->queue_lock, flags);
/* Make sure the queue isn't suspended, as that will deadlock */
mmc_queue_resume(mq);
/* Then terminate our worker thread */ /* Then terminate our worker thread */
kthread_stop(mq->thread); kthread_stop(mq->thread);
...@@ -226,7 +229,6 @@ void mmc_queue_suspend(struct mmc_queue *mq) ...@@ -226,7 +229,6 @@ void mmc_queue_suspend(struct mmc_queue *mq)
down(&mq->thread_sem); down(&mq->thread_sem);
} }
} }
EXPORT_SYMBOL(mmc_queue_suspend);
/** /**
* mmc_queue_resume - resume a previously suspended MMC request queue * mmc_queue_resume - resume a previously suspended MMC request queue
...@@ -247,4 +249,4 @@ void mmc_queue_resume(struct mmc_queue *mq) ...@@ -247,4 +249,4 @@ void mmc_queue_resume(struct mmc_queue *mq)
spin_unlock_irqrestore(q->queue_lock, flags); spin_unlock_irqrestore(q->queue_lock, flags);
} }
} }
EXPORT_SYMBOL(mmc_queue_resume);
#
# MMC core configuration
#
config MMC_UNSAFE_RESUME
bool "Allow unsafe resume (DANGEROUS)"
depends on MMC != n
help
If you say Y here, the MMC layer will assume that all cards
stayed in their respective slots during the suspend. The
normal behaviour is to remove them at suspend and
redetecting them at resume. Breaking this assumption will
in most cases result in data corruption.
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
#
# Makefile for the kernel mmc core.
#
ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
obj-$(CONFIG_MMC) += mmc_core.o
mmc_core-y := core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o
/*
* linux/drivers/mmc/core/core.c
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/pagemap.h>
#include <linux/err.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include "core.h"
#include "sysfs.h"
#include "mmc_ops.h"
#include "sd_ops.h"
extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
/**
* mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request
* @mrq: MMC request which request
*
* MMC drivers should call this function when they have completed
* their processing of a request.
*/
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n",
mmc_hostname(host), cmd->opcode, err,
mrq->data ? mrq->data->error : 0,
mrq->stop ? mrq->stop->error : 0,
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
if (err && cmd->retries) {
cmd->retries--;
cmd->error = 0;
host->ops->request(host, mrq);
} else if (mrq->done) {
mrq->done(mrq);
}
}
EXPORT_SYMBOL(mmc_request_done);
/**
* mmc_start_request - start a command on a host
* @host: MMC host to start command on
* @mrq: MMC request to start
*
* Queue a command on the specified host. We expect the
* caller to be holding the host lock with interrupts disabled.
*/
void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned int i, sz;
#endif
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->cmd->opcode,
mrq->cmd->arg, mrq->cmd->flags);
WARN_ON(!host->claimed);
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
BUG_ON(mrq->data->blksz > host->max_blk_size);
BUG_ON(mrq->data->blocks > host->max_blk_count);
BUG_ON(mrq->data->blocks * mrq->data->blksz >
host->max_req_size);
#ifdef CONFIG_MMC_DEBUG
sz = 0;
for (i = 0;i < mrq->data->sg_len;i++)
sz += mrq->data->sg[i].length;
BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);
#endif
mrq->cmd->data = mrq->data;
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
}
host->ops->request(host, mrq);
}
EXPORT_SYMBOL(mmc_start_request);
static void mmc_wait_done(struct mmc_request *mrq)
{
complete(mrq->done_data);
}
int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
DECLARE_COMPLETION_ONSTACK(complete);
mrq->done_data = &complete;
mrq->done = mmc_wait_done;
mmc_start_request(host, mrq);
wait_for_completion(&complete);
return 0;
}
EXPORT_SYMBOL(mmc_wait_for_req);
/**
* mmc_wait_for_cmd - start a command and wait for completion
* @host: MMC host to start command
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Start a new MMC command for a host, and wait for the command
* to complete. Return any error that occurred while the command
* was executing. Do not attempt to parse the response.
*/
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
BUG_ON(!host->claimed);
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = retries;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
return cmd->error;
}
EXPORT_SYMBOL(mmc_wait_for_cmd);
/**
* mmc_set_data_timeout - set the timeout for a data command
* @data: data phase for command
* @card: the MMC card associated with the data transfer
* @write: flag to differentiate reads from writes
*/
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
int write)
{
unsigned int mult;
/*
* SD cards use a 100 multiplier rather than 10
*/
mult = mmc_card_sd(card) ? 100 : 10;
/*
* Scale up the multiplier (and therefore the timeout) by
* the r2w factor for writes.
*/
if (write)
mult <<= card->csd.r2w_factor;
data->timeout_ns = card->csd.tacc_ns * mult;
data->timeout_clks = card->csd.tacc_clks * mult;
/*
* SD cards also have an upper limit on the timeout.
*/
if (mmc_card_sd(card)) {
unsigned int timeout_us, limit_us;
timeout_us = data->timeout_ns / 1000;
timeout_us += data->timeout_clks * 1000 /
(card->host->ios.clock / 1000);
if (write)
limit_us = 250000;
else
limit_us = 100000;
/*
* SDHC cards always use these fixed values.
*/
if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
data->timeout_ns = limit_us * 1000;
data->timeout_clks = 0;
}
}
}
EXPORT_SYMBOL(mmc_set_data_timeout);
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @card: mmc card to claim host for
*
* Claim a host for a set of operations. If a valid card
* is passed and this wasn't the last card selected, select
* the card before returning.
*
* Note: you should use mmc_card_claim_host or mmc_claim_host.
*/
void mmc_claim_host(struct mmc_host *host)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
add_wait_queue(&host->wq, &wait);
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!host->claimed)
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);
host->claimed = 1;
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
}
EXPORT_SYMBOL(mmc_claim_host);
/**
* mmc_release_host - release a host
* @host: mmc host to release
*
* Release a MMC host, allowing others to claim the host
* for their operations.
*/
void mmc_release_host(struct mmc_host *host)
{
unsigned long flags;
BUG_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags);
host->claimed = 0;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);
}
EXPORT_SYMBOL(mmc_release_host);
/*
* Internal function that does the actual ios call to the host driver,
* optionally printing some debug output.
*/
static inline void mmc_set_ios(struct mmc_host *host)
{
struct mmc_ios *ios = &host->ios;
pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
"width %u timing %u\n",
mmc_hostname(host), ios->clock, ios->bus_mode,
ios->power_mode, ios->chip_select, ios->vdd,
ios->bus_width, ios->timing);
host->ops->set_ios(host, ios);
}
/*
* Control chip select pin on a host.
*/
void mmc_set_chip_select(struct mmc_host *host, int mode)
{
host->ios.chip_select = mode;
mmc_set_ios(host);
}
/*
* Sets the host clock to the highest possible frequency that
* is below "hz".
*/
void mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
WARN_ON(hz < host->f_min);
if (hz > host->f_max)
hz = host->f_max;
host->ios.clock = hz;
mmc_set_ios(host);
}
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
host->ios.bus_mode = mode;
mmc_set_ios(host);
}
/*
* Change data bus width of a host.
*/
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
host->ios.bus_width = width;
mmc_set_ios(host);
}
/*
* Mask off any voltages we don't support and select
* the lowest voltage
*/
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
int bit;
ocr &= host->ocr_avail;
bit = ffs(ocr);
if (bit) {
bit -= 1;
ocr &= 3 << bit;
host->ios.vdd = bit;
mmc_set_ios(host);
} else {
ocr = 0;
}
return ocr;
}
/*
* Select timing parameters for host.
*/
void mmc_set_timing(struct mmc_host *host, unsigned int timing)
{
host->ios.timing = timing;
mmc_set_ios(host);
}
/*
* Allocate a new MMC card
*/
struct mmc_card *mmc_alloc_card(struct mmc_host *host)
{
struct mmc_card *card;
card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
mmc_init_card(card, host);
return card;
}
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
* We then wait a bit for the power to stabilise. Finally,
* enable the bus drivers and clock to the card.
*
* We must _NOT_ enable the clock prior to power stablising.
*
* If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage.
*/
static void mmc_power_up(struct mmc_host *host)
{
int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
mmc_delay(1);
host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
mmc_delay(2);
}
static void mmc_power_off(struct mmc_host *host)
{
host->ios.clock = 0;
host->ios.vdd = 0;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.power_mode = MMC_POWER_OFF;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
}
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
*/
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
unsigned long flags;
BUG_ON(!host);
BUG_ON(!ops);
BUG_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags);
BUG_ON(host->bus_ops);
BUG_ON(host->bus_refs);
host->bus_ops = ops;
host->bus_refs = 1;
host->bus_dead = 0;
spin_unlock_irqrestore(&host->lock, flags);
}
/*
* Remove the current bus handler from a host. Assumes that there are
* no interesting cards left, so the bus is powered down.
*/
void mmc_detach_bus(struct mmc_host *host)
{
unsigned long flags;
BUG_ON(!host);
BUG_ON(!host->claimed);
BUG_ON(!host->bus_ops);
spin_lock_irqsave(&host->lock, flags);
host->bus_dead = 1;
spin_unlock_irqrestore(&host->lock, flags);
mmc_power_off(host);
mmc_bus_put(host);
}
/*
* Cleanup when the last reference to the bus operator is dropped.
*/
void __mmc_release_bus(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(host->bus_refs);
BUG_ON(!host->bus_dead);
host->bus_ops = NULL;
}
/**
* mmc_detect_change - process change of state on a MMC socket
* @host: host which changed state.
* @delay: optional delay to wait before detection (jiffies)
*
* All we know is that card(s) have been inserted or removed
* from the socket(s). We don't know which socket or cards.
*/
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
mmc_claim_host(host);
BUG_ON(host->removed);
mmc_release_host(host);
#endif
mmc_schedule_delayed_work(&host->detect, delay);
}
EXPORT_SYMBOL(mmc_detect_change);
static void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
u32 ocr;
int err;
mmc_bus_get(host);
if (host->bus_ops == NULL) {
/*
* Only we can add a new handler, so it's safe to
* release the lock here.
*/
mmc_bus_put(host);
mmc_claim_host(host);
mmc_power_up(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
err = mmc_send_app_op_cond(host, 0, &ocr);
if (err == MMC_ERR_NONE) {
if (mmc_attach_sd(host, ocr))
mmc_power_off(host);
} else {
/*
* If we fail to detect any SD cards then try
* searching for MMC cards.
*/
err = mmc_send_op_cond(host, 0, &ocr);
if (err == MMC_ERR_NONE) {
if (mmc_attach_mmc(host, ocr))
mmc_power_off(host);
} else {
mmc_power_off(host);
mmc_release_host(host);
}
}
} else {
if (host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host);
mmc_bus_put(host);
}
}
/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
* @dev: pointer to host device model structure
*
* Initialise the per-host structure.
*/
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
struct mmc_host *host;
host = mmc_alloc_host_sysfs(extra, dev);
if (host) {
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
*/
host->max_hw_segs = 1;
host->max_phys_segs = 1;
host->max_seg_size = PAGE_CACHE_SIZE;
host->max_req_size = PAGE_CACHE_SIZE;
host->max_blk_size = 512;
host->max_blk_count = PAGE_CACHE_SIZE / 512;
}
return host;
}
EXPORT_SYMBOL(mmc_alloc_host);
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
*/
int mmc_add_host(struct mmc_host *host)
{
int ret;
ret = mmc_add_host_sysfs(host);
if (ret == 0) {
mmc_power_off(host);
mmc_detect_change(host, 0);
}
return ret;
}
EXPORT_SYMBOL(mmc_add_host);
/**
* mmc_remove_host - remove host hardware
* @host: mmc host
*
* Unregister and remove all cards associated with this host,
* and power down the MMC bus.
*/
void mmc_remove_host(struct mmc_host *host)
{
#ifdef CONFIG_MMC_DEBUG
mmc_claim_host(host);
host->removed = 1;
mmc_release_host(host);
#endif
mmc_flush_scheduled_work();
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->remove)
host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
mmc_bus_put(host);
BUG_ON(host->card);
mmc_power_off(host);
mmc_remove_host_sysfs(host);
}
EXPORT_SYMBOL(mmc_remove_host);
/**
* mmc_free_host - free the host structure
* @host: mmc host
*
* Free the host once all references to it have been dropped.
*/
void mmc_free_host(struct mmc_host *host)
{
mmc_free_host_sysfs(host);
}
EXPORT_SYMBOL(mmc_free_host);
#ifdef CONFIG_PM
/**
* mmc_suspend_host - suspend a host
* @host: mmc host
* @state: suspend mode (PM_SUSPEND_xxx)
*/
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
{
mmc_flush_scheduled_work();
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend)
host->bus_ops->suspend(host);
if (!host->bus_ops->resume) {
if (host->bus_ops->remove)
host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
mmc_bus_put(host);
mmc_power_off(host);
return 0;
}
EXPORT_SYMBOL(mmc_suspend_host);
/**
* mmc_resume_host - resume a previously suspended host
* @host: mmc host
*/
int mmc_resume_host(struct mmc_host *host)
{
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
mmc_power_up(host);
BUG_ON(!host->bus_ops->resume);
host->bus_ops->resume(host);
}
mmc_bus_put(host);
/*
* We add a slight delay here so that resume can progress
* in parallel.
*/
mmc_detect_change(host, 1);
return 0;
}
EXPORT_SYMBOL(mmc_resume_host);
#endif
MODULE_LICENSE("GPL");
/*
* linux/drivers/mmc/core/core.h
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright 2007 Pierre Ossman
*
* 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.
*/
#ifndef _MMC_CORE_CORE_H
#define _MMC_CORE_CORE_H
#include <linux/delay.h>
#define MMC_CMD_RETRIES 3
struct mmc_bus_ops {
void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *);
void (*resume)(struct mmc_host *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);
void __mmc_release_bus(struct mmc_host *host);
static inline void mmc_bus_get(struct mmc_host *host)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->bus_refs++;
spin_unlock_irqrestore(&host->lock, flags);
}
static inline void mmc_bus_put(struct mmc_host *host)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->bus_refs--;
if ((host->bus_refs == 0) && host->bus_ops)
__mmc_release_bus(host);
spin_unlock_irqrestore(&host->lock, flags);
}
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
struct mmc_card *mmc_alloc_card(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms)
{
if (ms < 1000 / HZ) {
cond_resched();
mdelay(ms);
} else {
msleep(ms);
}
}
#endif
/*
* linux/drivers/mmc/mmc.c
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include "core.h"
#include "sysfs.h"
#include "mmc_ops.h"
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
};
static const unsigned char tran_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static const unsigned int tacc_exp[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static const unsigned int tacc_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
const int __off = 3 - ((start) / 32); \
const int __shft = (start) & 31; \
u32 __res; \
\
__res = resp[__off] >> __shft; \
if (__size + __shft > 32) \
__res |= resp[__off-1] << ((32 - __shft) % 32); \
__res & __mask; \
})
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
static int mmc_decode_cid(struct mmc_card *card)
{
u32 *resp = card->raw_cid;
/*
* The selection of the format here is based upon published
* specs from sandisk and from what people have reported.
*/
switch (card->csd.mmca_vsn) {
case 0: /* MMC v1.0 - v1.2 */
case 1: /* MMC v1.4 */
card->cid.manfid = UNSTUFF_BITS(resp, 104, 24);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8);
card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4);
card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4);
card->cid.serial = UNSTUFF_BITS(resp, 16, 24);
card->cid.month = UNSTUFF_BITS(resp, 12, 4);
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
break;
case 2: /* MMC v2.0 - v2.2 */
case 3: /* MMC v3.1 - v3.3 */
case 4: /* MMC v4 */
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
card->cid.month = UNSTUFF_BITS(resp, 12, 4);
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
break;
default:
printk("%s: card has unknown MMCA version %d\n",
mmc_hostname(card->host), card->csd.mmca_vsn);
return -EINVAL;
}
return 0;
}
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
static int mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
unsigned int e, m, csd_struct;
u32 *resp = card->raw_csd;
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
*/
csd_struct = UNSTUFF_BITS(resp, 126, 2);
if (csd_struct != 1 && csd_struct != 2) {
printk("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct);
return -EINVAL;
}
csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4);
m = UNSTUFF_BITS(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3);
csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
m = UNSTUFF_BITS(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
e = UNSTUFF_BITS(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
return 0;
}
/*
* Read and decode extended CSD.
*/
static int mmc_read_ext_csd(struct mmc_card *card)
{
int err;
u8 *ext_csd;
BUG_ON(!card);
err = MMC_ERR_FAILED;
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return MMC_ERR_NONE;
/*
* As the ext_csd is so large and mostly unused, we don't store the
* raw block in mmc_card.
*/
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) {
printk(KERN_ERR "%s: could not allocate a buffer to "
"receive the ext_csd. mmc v4 cards will be "
"treated as v3.\n", mmc_hostname(card->host));
return MMC_ERR_FAILED;
}
err = mmc_send_ext_csd(card, ext_csd);
if (err != MMC_ERR_NONE) {
/*
* High capacity cards should have this "magic" size
* stored in their CSD.
*/
if (card->csd.capacity == (4096 * 512)) {
printk(KERN_ERR "%s: unable to read EXT_CSD "
"on a possible high capacity card. "
"Card will be ignored.\n",
mmc_hostname(card->host));
} else {
printk(KERN_WARNING "%s: unable to read "
"EXT_CSD, performance might "
"suffer.\n",
mmc_hostname(card->host));
err = MMC_ERR_NONE;
}
goto out;
}
card->ext_csd.sectors =
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
if (card->ext_csd.sectors)
mmc_card_set_blockaddr(card);
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
break;
case EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 26000000;
break;
default:
/* MMC v4 spec says this cannot happen */
printk(KERN_WARNING "%s: card is mmc v4 but doesn't "
"support any high-speed modes.\n",
mmc_hostname(card->host));
goto out;
}
out:
kfree(ext_csd);
return err;
}
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "curcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
u32 cid[4];
unsigned int max_dtr;
BUG_ON(!host);
BUG_ON(!host->claimed);
/*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to
* respond.
*/
mmc_go_idle(host);
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
if (err != MMC_ERR_NONE)
goto err;
/*
* Fetch CID from card.
*/
err = mmc_all_send_cid(host, cid);
if (err != MMC_ERR_NONE)
goto err;
if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
goto err;
card = oldcard;
} else {
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host);
if (IS_ERR(card))
goto err;
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
}
/*
* Set card RCA.
*/
err = mmc_set_relative_addr(card);
if (err != MMC_ERR_NONE)
goto free_card;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
if (!oldcard) {
/*
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd);
if (err != MMC_ERR_NONE)
goto free_card;
err = mmc_decode_csd(card);
if (err < 0)
goto free_card;
err = mmc_decode_cid(card);
if (err < 0)
goto free_card;
}
/*
* Select card, as all following commands rely on that.
*/
err = mmc_select_card(card);
if (err != MMC_ERR_NONE)
goto free_card;
if (!oldcard) {
/*
* Fetch and process extened CSD.
*/
err = mmc_read_ext_csd(card);
if (err != MMC_ERR_NONE)
goto free_card;
}
/*
* Activate high speed (if supported)
*/
if ((card->ext_csd.hs_max_dtr != 0) &&
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 1);
if (err != MMC_ERR_NONE)
goto free_card;
mmc_card_set_highspeed(card);
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
}
/*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
if (mmc_card_highspeed(card)) {
if (max_dtr > card->ext_csd.hs_max_dtr)
max_dtr = card->ext_csd.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
max_dtr = card->csd.max_dtr;
}
mmc_set_clock(host, max_dtr);
/*
* Activate wide bus (if supported).
*/
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
(host->caps & MMC_CAP_4_BIT_DATA)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
if (err != MMC_ERR_NONE)
goto free_card;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
}
if (!oldcard)
host->card = card;
return MMC_ERR_NONE;
free_card:
if (!oldcard)
mmc_remove_card(card);
err:
return MMC_ERR_FAILED;
}
/*
* Host is being removed. Free up the current card.
*/
static void mmc_remove(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(!host->card);
mmc_remove_card(host->card);
host->card = NULL;
}
/*
* Card detection callback from host.
*/
static void mmc_detect(struct mmc_host *host)
{
int err;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
/*
* Just check if our card has been removed.
*/
err = mmc_send_status(host->card, NULL);
mmc_release_host(host);
if (err != MMC_ERR_NONE) {
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
#ifdef CONFIG_MMC_UNSAFE_RESUME
/*
* Suspend callback from host.
*/
static void mmc_suspend(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host);
}
/*
* Resume callback from host.
*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
*/
static void mmc_resume(struct mmc_host *host)
{
int err;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
err = mmc_sd_init_card(host, host->ocr, host->card);
if (err != MMC_ERR_NONE) {
mmc_remove_card(host->card);
host->card = NULL;
mmc_detach_bus(host);
}
mmc_release_host(host);
}
#else
#define mmc_suspend NULL
#define mmc_resume NULL
#endif
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
};
/*
* Starting point for MMC card init.
*/
int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
{
int err;
BUG_ON(!host);
BUG_ON(!host->claimed);
mmc_attach_bus(host, &mmc_ops);
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
host->ocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage of the card?
*/
if (!host->ocr)
goto err;
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, NULL);
if (err != MMC_ERR_NONE)
goto err;
mmc_release_host(host);
err = mmc_register_card(host->card);
if (err)
goto reclaim_host;
return 0;
reclaim_host:
mmc_claim_host(host);
mmc_remove_card(host->card);
host->card = NULL;
err:
mmc_detach_bus(host);
mmc_release_host(host);
return 0;
}
/*
* linux/drivers/mmc/mmc_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/types.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include "core.h"
#include "mmc_ops.h"
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SELECT_CARD;
if (card) {
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
}
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_select_card(struct mmc_card *card)
{
BUG_ON(!card);
return _mmc_select_card(card->host, card);
}
int mmc_deselect_cards(struct mmc_host *host)
{
return _mmc_select_card(host, NULL);
}
int mmc_go_idle(struct mmc_host *host)
{
int err;
struct mmc_command cmd;
mmc_set_chip_select(host, MMC_CS_HIGH);
mmc_delay(1);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
err = mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1);
mmc_set_chip_select(host, MMC_CS_DONTCARE);
mmc_delay(1);
return err;
}
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(!cid);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
memcpy(cid, cmd.resp, sizeof(u32) * 4);
return MMC_ERR_NONE;
}
int mmc_set_relative_addr(struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
BUG_ON(!csd);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_CSD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
memcpy(csd, cmd.resp, sizeof(u32) * 4);
return MMC_ERR_NONE;
}
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(!card->host);
BUG_ON(!ext_csd);
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 512;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, ext_csd, 512);
mmc_set_data_timeout(&data, card, 0);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE)
return cmd.error;
if (data.error != MMC_ERR_NONE)
return data.error;
return MMC_ERR_NONE;
}
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) |
(value << 8) |
set;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
if (status)
*status = cmd.resp[0];
return MMC_ERR_NONE;
}
/*
* linux/drivers/mmc/mmc_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef _MMC_MMC_OPS_H
#define _MMC_MMC_OPS_H
int mmc_select_card(struct mmc_card *card);
int mmc_deselect_cards(struct mmc_host *host);
int mmc_go_idle(struct mmc_host *host);
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
int mmc_set_relative_addr(struct mmc_card *card);
int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status);
#endif
/*
* linux/drivers/mmc/sd.c
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include "core.h"
#include "sysfs.h"
#include "mmc_ops.h"
#include "sd_ops.h"
#include "core.h"
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
};
static const unsigned char tran_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static const unsigned int tacc_exp[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static const unsigned int tacc_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
const int __off = 3 - ((start) / 32); \
const int __shft = (start) & 31; \
u32 __res; \
\
__res = resp[__off] >> __shft; \
if (__size + __shft > 32) \
__res |= resp[__off-1] << ((32 - __shft) % 32); \
__res & __mask; \
})
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
static void mmc_decode_cid(struct mmc_card *card)
{
u32 *resp = card->raw_cid;
memset(&card->cid, 0, sizeof(struct mmc_cid));
/*
* SD doesn't currently have a version field so we will
* have to assume we can parse this.
*/
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4);
card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4);
card->cid.serial = UNSTUFF_BITS(resp, 24, 32);
card->cid.year = UNSTUFF_BITS(resp, 12, 8);
card->cid.month = UNSTUFF_BITS(resp, 8, 4);
card->cid.year += 2000; /* SD cards year offset */
}
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
static int mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
unsigned int e, m, csd_struct;
u32 *resp = card->raw_csd;
csd_struct = UNSTUFF_BITS(resp, 126, 2);
switch (csd_struct) {
case 0:
m = UNSTUFF_BITS(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3);
csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
m = UNSTUFF_BITS(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
e = UNSTUFF_BITS(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
break;
case 1:
/*
* This is a block-addressed SDHC card. Most
* interesting fields are unused and have fixed
* values. To avoid getting tripped by buggy cards,
* we assume those fixed values ourselves.
*/
mmc_card_set_blockaddr(card);
csd->tacc_ns = 0; /* Unused */
csd->tacc_clks = 0; /* Unused */
m = UNSTUFF_BITS(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
m = UNSTUFF_BITS(resp, 48, 22);
csd->capacity = (1 + m) << 10;
csd->read_blkbits = 9;
csd->read_partial = 0;
csd->write_misalign = 0;
csd->read_misalign = 0;
csd->r2w_factor = 4; /* Unused */
csd->write_blkbits = 9;
csd->write_partial = 0;
break;
default:
printk("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct);
return -EINVAL;
}
return 0;
}
/*
* Given a 64-bit response, decode to our card SCR structure.
*/
static int mmc_decode_scr(struct mmc_card *card)
{
struct sd_scr *scr = &card->scr;
unsigned int scr_struct;
u32 resp[4];
BUG_ON(!mmc_card_sd(card));
resp[3] = card->raw_scr[1];
resp[2] = card->raw_scr[0];
scr_struct = UNSTUFF_BITS(resp, 60, 4);
if (scr_struct != 0) {
printk("%s: unrecognised SCR structure version %d\n",
mmc_hostname(card->host), scr_struct);
return -EINVAL;
}
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
return 0;
}
/*
* Fetches and decodes switch information
*/
static int mmc_read_switch(struct mmc_card *card)
{
int err;
u8 *status;
err = MMC_ERR_FAILED;
status = kmalloc(64, GFP_KERNEL);
if (!status) {
printk("%s: could not allocate a buffer for switch "
"capabilities.\n",
mmc_hostname(card->host));
return err;
}
err = mmc_sd_switch(card, 0, 0, 1, status);
if (err != MMC_ERR_NONE) {
/*
* Card not supporting high-speed will ignore the
* command.
*/
err = MMC_ERR_NONE;
goto out;
}
if (status[13] & 0x02)
card->sw_caps.hs_max_dtr = 50000000;
out:
kfree(status);
return err;
}
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
static int mmc_switch_hs(struct mmc_card *card)
{
int err;
u8 *status;
if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
return MMC_ERR_NONE;
if (card->sw_caps.hs_max_dtr == 0)
return MMC_ERR_NONE;
err = MMC_ERR_FAILED;
status = kmalloc(64, GFP_KERNEL);
if (!status) {
printk("%s: could not allocate a buffer for switch "
"capabilities.\n",
mmc_hostname(card->host));
return err;
}
err = mmc_sd_switch(card, 1, 0, 1, status);
if (err != MMC_ERR_NONE)
goto out;
if ((status[16] & 0xF) != 1) {
printk(KERN_WARNING "%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(card->host));
} else {
mmc_card_set_highspeed(card);
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
}
out:
kfree(status);
return err;
}
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "curcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
u32 cid[4];
unsigned int max_dtr;
BUG_ON(!host);
BUG_ON(!host->claimed);
/*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to
* respond.
*/
mmc_go_idle(host);
/*
* If SD_SEND_IF_COND indicates an SD 2.0
* compliant card and we should set bit 30
* of the ocr to indicate that we can handle
* block-addressed SDHC cards.
*/
err = mmc_send_if_cond(host, ocr);
if (err == MMC_ERR_NONE)
ocr |= 1 << 30;
err = mmc_send_app_op_cond(host, ocr, NULL);
if (err != MMC_ERR_NONE)
goto err;
/*
* Fetch CID from card.
*/
err = mmc_all_send_cid(host, cid);
if (err != MMC_ERR_NONE)
goto err;
if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
goto err;
card = oldcard;
} else {
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host);
if (IS_ERR(card))
goto err;
card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
}
/*
* Set card RCA.
*/
err = mmc_send_relative_addr(host, &card->rca);
if (err != MMC_ERR_NONE)
goto free_card;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
if (!oldcard) {
/*
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd);
if (err != MMC_ERR_NONE)
goto free_card;
err = mmc_decode_csd(card);
if (err < 0)
goto free_card;
mmc_decode_cid(card);
}
/*
* Select card, as all following commands rely on that.
*/
err = mmc_select_card(card);
if (err != MMC_ERR_NONE)
goto free_card;
if (!oldcard) {
/*
* Fetch SCR from card.
*/
err = mmc_app_send_scr(card, card->raw_scr);
if (err != MMC_ERR_NONE)
goto free_card;
err = mmc_decode_scr(card);
if (err < 0)
goto free_card;
/*
* Fetch switch information from card.
*/
err = mmc_read_switch(card);
if (err != MMC_ERR_NONE)
goto free_card;
}
/*
* Attempt to change to high-speed (if supported)
*/
err = mmc_switch_hs(card);
if (err != MMC_ERR_NONE)
goto free_card;
/*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
if (mmc_card_highspeed(card)) {
if (max_dtr > card->sw_caps.hs_max_dtr)
max_dtr = card->sw_caps.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
max_dtr = card->csd.max_dtr;
}
mmc_set_clock(host, max_dtr);
/*
* Switch to wider bus (if supported).
*/
if ((host->caps && MMC_CAP_4_BIT_DATA) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err != MMC_ERR_NONE)
goto free_card;
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
if (!oldcard)
host->card = card;
return MMC_ERR_NONE;
free_card:
if (!oldcard)
mmc_remove_card(card);
err:
return MMC_ERR_FAILED;
}
/*
* Host is being removed. Free up the current card.
*/
static void mmc_sd_remove(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(!host->card);
mmc_remove_card(host->card);
host->card = NULL;
}
/*
* Card detection callback from host.
*/
static void mmc_sd_detect(struct mmc_host *host)
{
int err;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
/*
* Just check if our card has been removed.
*/
err = mmc_send_status(host->card, NULL);
mmc_release_host(host);
if (err != MMC_ERR_NONE) {
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
#ifdef CONFIG_MMC_UNSAFE_RESUME
/*
* Suspend callback from host.
*/
static void mmc_sd_suspend(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host);
}
/*
* Resume callback from host.
*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
*/
static void mmc_sd_resume(struct mmc_host *host)
{
int err;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
err = mmc_sd_init_card(host, host->ocr, host->card);
if (err != MMC_ERR_NONE) {
mmc_remove_card(host->card);
host->card = NULL;
mmc_detach_bus(host);
}
mmc_release_host(host);
}
#else
#define mmc_sd_suspend NULL
#define mmc_sd_resume NULL
#endif
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
};
/*
* Starting point for SD card init.
*/
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
int err;
BUG_ON(!host);
BUG_ON(!host->claimed);
mmc_attach_bus(host, &mmc_sd_ops);
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
if (ocr & MMC_VDD_165_195) {
printk(KERN_WARNING "%s: SD card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}
host->ocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr)
goto err;
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, NULL);
if (err != MMC_ERR_NONE)
goto err;
mmc_release_host(host);
err = mmc_register_card(host->card);
if (err)
goto reclaim_host;
return 0;
reclaim_host:
mmc_claim_host(host);
mmc_remove_card(host->card);
host->card = NULL;
err:
mmc_detach_bus(host);
mmc_release_host(host);
return 0;
}
/*
* linux/drivers/mmc/sd_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/types.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include "core.h"
#include "sd_ops.h"
/**
* mmc_wait_for_app_cmd - start an application command and wait for
completion
* @host: MMC host to start command
* @rca: RCA to send MMC_APP_CMD to
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Sends a MMC_APP_CMD, checks the card response, sends the command
* in the parameter and waits for it to complete. Return any error
* that occurred while the command was executing. Do not attempt to
* parse the response.
*/
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
int i, err;
BUG_ON(!cmd);
BUG_ON(retries < 0);
err = MMC_ERR_INVALID;
/*
* We have to resend MMC_APP_CMD for each attempt so
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
memset(&mrq, 0, sizeof(struct mmc_request));
err = mmc_app_cmd(host, card);
if (err != MMC_ERR_NONE)
continue;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = 0;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
err = cmd->error;
if (cmd->error == MMC_ERR_NONE)
break;
}
return err;
}
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(card && (card->host != host));
cmd.opcode = MMC_APP_CMD;
if (card) {
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
}
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
return err;
/* Check that card supported application commands */
if (!(cmd.resp[0] & R1_APP_CMD))
return MMC_ERR_FAILED;
return MMC_ERR_NONE;
}
int mmc_app_set_bus_width(struct mmc_card *card, int width)
{
int err;
struct mmc_command cmd;
BUG_ON(!card);
BUG_ON(!card->host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
switch (width) {
case MMC_BUS_WIDTH_1:
cmd.arg = SD_BUS_WIDTH_1;
break;
case MMC_BUS_WIDTH_4:
cmd.arg = SD_BUS_WIDTH_4;
break;
default:
return MMC_ERR_INVALID;
}
err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
return MMC_ERR_NONE;
}
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
struct mmc_command cmd;
int err;
static const u8 test_pattern = 0xAA;
/*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
* before SD_APP_OP_COND. This command will harmlessly fail for
* SD 1.0 cards.
*/
cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
return err;
if ((cmd.resp[0] & 0xFF) != test_pattern)
return MMC_ERR_FAILED;
return MMC_ERR_NONE;
}
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(!rca);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
*rca = cmd.resp[0] >> 16;
return MMC_ERR_NONE;
}
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
{
int err;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(!card->host);
BUG_ON(!scr);
err = mmc_app_cmd(card->host, card);
if (err != MMC_ERR_NONE)
return err;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 8;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, scr, 8);
mmc_set_data_timeout(&data, card, 0);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE)
return cmd.error;
if (data.error != MMC_ERR_NONE)
return data.error;
scr[0] = ntohl(scr[0]);
scr[1] = ntohl(scr[1]);
return MMC_ERR_NONE;
}
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(!card->host);
mode = !!mode;
value &= 0xF;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = SD_SWITCH;
cmd.arg = mode << 31 | 0x00FFFFFF;
cmd.arg &= ~(0xF << (group * 4));
cmd.arg |= value << (group * 4);
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, resp, 64);
mmc_set_data_timeout(&data, card, 0);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE)
return cmd.error;
if (data.error != MMC_ERR_NONE)
return data.error;
return MMC_ERR_NONE;
}
/*
* linux/drivers/mmc/sd_ops.h
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef _MMC_SD_OPS_H
#define _MMC_SD_OPS_H
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
int mmc_app_set_bus_width(struct mmc_card *card, int width);
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
#endif
/* /*
* linux/drivers/mmc/mmc_sysfs.c * linux/drivers/mmc/core/sysfs.c
* *
* Copyright (C) 2003 Russell King, All Rights Reserved. * Copyright (C) 2003 Russell King, All Rights Reserved.
* *
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "mmc.h" #include "sysfs.h"
#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
...@@ -72,12 +72,11 @@ static void mmc_release_card(struct device *dev) ...@@ -72,12 +72,11 @@ static void mmc_release_card(struct device *dev)
/* /*
* This currently matches any MMC driver to any MMC card - drivers * This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their * themselves make the decision whether to drive this card in their
* probe method. However, we force "bad" cards to fail. * probe method.
*/ */
static int mmc_bus_match(struct device *dev, struct device_driver *drv) static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{ {
struct mmc_card *card = dev_to_mmc_card(dev); return 1;
return !mmc_card_bad(card);
} }
static int static int
...@@ -217,6 +216,8 @@ int mmc_register_card(struct mmc_card *card) ...@@ -217,6 +216,8 @@ int mmc_register_card(struct mmc_card *card)
device_del(&card->dev); device_del(&card->dev);
} }
} }
if (ret == 0)
mmc_card_set_present(card);
return ret; return ret;
} }
......
/* /*
* linux/drivers/mmc/mmc.h * linux/drivers/mmc/core/sysfs.h
* *
* Copyright (C) 2003 Russell King, All Rights Reserved. * Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright 2007 Pierre Ossman
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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.
*/ */
#ifndef _MMC_H #ifndef _MMC_CORE_SYSFS_H
#define _MMC_H #define _MMC_CORE_SYSFS_H
/* core-internal functions */
void mmc_init_card(struct mmc_card *card, struct mmc_host *host); void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
int mmc_register_card(struct mmc_card *card); int mmc_register_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card); void mmc_remove_card(struct mmc_card *card);
...@@ -22,4 +23,5 @@ void mmc_free_host_sysfs(struct mmc_host *host); ...@@ -22,4 +23,5 @@ void mmc_free_host_sysfs(struct mmc_host *host);
int mmc_schedule_work(struct work_struct *work); int mmc_schedule_work(struct work_struct *work);
int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay);
void mmc_flush_scheduled_work(void); void mmc_flush_scheduled_work(void);
#endif #endif
#
# MMC/SD host controller drivers
#
comment "MMC/SD Host Controller Drivers"
depends on MMC
config MMC_ARMMMCI
tristate "ARM AMBA Multimedia Card Interface support"
depends on ARM_AMBA && MMC
help
This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
Interface (PL180 and PL181) support. If you have an ARM(R)
platform with a Multimedia Card slot, say Y or M here.
If unsure, say N.
config MMC_PXA
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
depends on ARCH_PXA && MMC
help
This selects the Intel(R) PXA(R) Multimedia card Interface.
If you have a PXA(R) platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_SDHCI
tristate "Secure Digital Host Controller Interface support (EXPERIMENTAL)"
depends on PCI && MMC && EXPERIMENTAL
help
This select the generic Secure Digital Host Controller Interface.
It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
and Toshiba(R). Most controllers found in laptops are of this type.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP && MMC
select TPS65010 if MACH_OMAP_H2
help
This selects the TI OMAP Multimedia card Interface.
If you have an OMAP board with a Multimedia Card slot,
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
help
This selects the Winbond(R) W83L51xD Secure digital and
Multimedia card Interface.
If you have a machine with a integrated W83L518D or W83L519D
SD/MMC card reader, say Y or M here.
If unsure, say N.
config MMC_AU1X
tristate "Alchemy AU1XX0 MMC Card Interface support"
depends on MMC && SOC_AU1200
help
This selects the AMD Alchemy(R) Multimedia card interface.
If you have a Alchemy platform with a MMC slot, say Y or M here.
If unsure, say N.
config MMC_AT91
tristate "AT91 SD/MMC Card Interface support"
depends on ARCH_AT91 && MMC
help
This selects the AT91 MCI controller.
If unsure, say N.
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
depends on ARCH_IMX && MMC
help
This selects the Motorola i.MX Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_TIFM_SD
tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)"
depends on MMC && EXPERIMENTAL && PCI
select TIFM_CORE
help
Say Y here if you want to be able to access MMC/SD cards with
the Texas Instruments(R) Flash Media card reader, found in many
laptops.
This option 'selects' (turns on, enables) 'TIFM_CORE', but you
probably also need appropriate card reader host adapter, such as
'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
(TIFM_7XX1)'.
To compile this driver as a module, choose M here: the
module will be called tifm_sd.
#
# Makefile for MMC/SD host controller drivers
#
ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
...@@ -67,7 +67,6 @@ ...@@ -67,7 +67,6 @@
#include <linux/atmel_pdc.h> #include <linux/atmel_pdc.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
......
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h> #include <asm/mach-au1x00/au1xxx_dbdma.h>
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <asm/dma.h> #include <asm/dma.h>
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <linux/clk.h> #include <linux/clk.h>
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/clk.h> #include <linux/clk.h>
...@@ -605,7 +604,7 @@ static void mmc_omap_switch_handler(struct work_struct *work) ...@@ -605,7 +604,7 @@ static void mmc_omap_switch_handler(struct work_struct *work)
} }
if (mmc_omap_cover_is_open(host)) { if (mmc_omap_cover_is_open(host)) {
if (!complained) { if (!complained) {
dev_info(mmc_dev(host->mmc), "cover is open"); dev_info(mmc_dev(host->mmc), "cover is open\n");
complained = 1; complained = 1;
} }
if (mmc_omap_enable_poll) if (mmc_omap_enable_poll)
...@@ -937,48 +936,55 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on) ...@@ -937,48 +936,55 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on)
} }
} }
static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)
{ {
struct mmc_omap_host *host = mmc_priv(mmc); struct mmc_omap_host *host = mmc_priv(mmc);
int func_clk_rate = clk_get_rate(host->fclk);
int dsor; int dsor;
int realclock, i;
realclock = ios->clock;
if (ios->clock == 0) if (ios->clock == 0)
dsor = 0; return 0;
else {
int func_clk_rate = clk_get_rate(host->fclk);
dsor = func_clk_rate / realclock;
if (dsor < 1)
dsor = 1;
if (func_clk_rate / dsor > realclock) dsor = func_clk_rate / ios->clock;
dsor++; if (dsor < 1)
dsor = 1;
if (dsor > 250) if (func_clk_rate / dsor > ios->clock)
dsor = 250;
dsor++; dsor++;
if (ios->bus_width == MMC_BUS_WIDTH_4) if (dsor > 250)
dsor |= 1 << 15; dsor = 250;
} dsor++;
if (ios->bus_width == MMC_BUS_WIDTH_4)
dsor |= 1 << 15;
return dsor;
}
static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmc_omap_host *host = mmc_priv(mmc);
int dsor;
int i;
dsor = mmc_omap_calc_divisor(mmc, ios);
host->bus_mode = ios->bus_mode;
host->hw_bus_mode = host->bus_mode;
switch (ios->power_mode) { switch (ios->power_mode) {
case MMC_POWER_OFF: case MMC_POWER_OFF:
mmc_omap_power(host, 0); mmc_omap_power(host, 0);
break; break;
case MMC_POWER_UP: case MMC_POWER_UP:
case MMC_POWER_ON: /* Cannot touch dsor yet, just power up MMC */
mmc_omap_power(host, 1); mmc_omap_power(host, 1);
return;
case MMC_POWER_ON:
dsor |= 1 << 11; dsor |= 1 << 11;
break; break;
} }
host->bus_mode = ios->bus_mode;
host->hw_bus_mode = host->bus_mode;
clk_enable(host->fclk); clk_enable(host->fclk);
/* On insanely high arm_per frequencies something sometimes /* On insanely high arm_per frequencies something sometimes
...@@ -987,7 +993,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -987,7 +993,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* Writing to the CON register twice seems to do the trick. */ * Writing to the CON register twice seems to do the trick. */
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
OMAP_MMC_WRITE(host, CON, dsor); OMAP_MMC_WRITE(host, CON, dsor);
if (ios->power_mode == MMC_POWER_UP) { if (ios->power_mode == MMC_POWER_ON) {
/* Send clock cycles, poll completion */ /* Send clock cycles, poll completion */
OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, IE, 0);
OMAP_MMC_WRITE(host, STAT, 0xffff); OMAP_MMC_WRITE(host, STAT, 0xffff);
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/io.h> #include <asm/io.h>
......
/* /*
* linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver
* *
* Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved. * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/scatterlist.h> #include <asm/scatterlist.h>
...@@ -247,14 +246,13 @@ static void sdhci_read_block_pio(struct sdhci_host *host) ...@@ -247,14 +246,13 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
chunk_remain = min(blksize, 4); chunk_remain = min(blksize, 4);
} }
size = min(host->size, host->remain); size = min(host->remain, chunk_remain);
size = min(size, chunk_remain);
chunk_remain -= size; chunk_remain -= size;
blksize -= size; blksize -= size;
host->offset += size; host->offset += size;
host->remain -= size; host->remain -= size;
host->size -= size;
while (size) { while (size) {
*buffer = data & 0xFF; *buffer = data & 0xFF;
buffer++; buffer++;
...@@ -289,14 +287,13 @@ static void sdhci_write_block_pio(struct sdhci_host *host) ...@@ -289,14 +287,13 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
buffer = sdhci_sg_to_buffer(host) + host->offset; buffer = sdhci_sg_to_buffer(host) + host->offset;
while (blksize) { while (blksize) {
size = min(host->size, host->remain); size = min(host->remain, chunk_remain);
size = min(size, chunk_remain);
chunk_remain -= size; chunk_remain -= size;
blksize -= size; blksize -= size;
host->offset += size; host->offset += size;
host->remain -= size; host->remain -= size;
host->size -= size;
while (size) { while (size) {
data >>= 8; data >>= 8;
data |= (u32)*buffer << 24; data |= (u32)*buffer << 24;
...@@ -325,7 +322,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host) ...@@ -325,7 +322,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
BUG_ON(!host->data); BUG_ON(!host->data);
if (host->size == 0) if (host->num_sg == 0)
return; return;
if (host->data->flags & MMC_DATA_READ) if (host->data->flags & MMC_DATA_READ)
...@@ -339,10 +336,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) ...@@ -339,10 +336,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
else else
sdhci_write_block_pio(host); sdhci_write_block_pio(host);
if (host->size == 0) if (host->num_sg == 0)
break; break;
BUG_ON(host->num_sg == 0);
} }
DBG("PIO transfer complete.\n"); DBG("PIO transfer complete.\n");
...@@ -408,8 +403,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) ...@@ -408,8 +403,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
} else { } else {
host->size = data->blksz * data->blocks;
host->cur_sg = data->sg; host->cur_sg = data->sg;
host->num_sg = data->sg_len; host->num_sg = data->sg_len;
...@@ -473,10 +466,6 @@ static void sdhci_finish_data(struct sdhci_host *host) ...@@ -473,10 +466,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
"though there were blocks left.\n", "though there were blocks left.\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
data->error = MMC_ERR_FAILED; data->error = MMC_ERR_FAILED;
} else if (host->size != 0) {
printk(KERN_ERR "%s: %d bytes were left untransferred.\n",
mmc_hostname(host->mmc), host->size);
data->error = MMC_ERR_FAILED;
} }
DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered);
...@@ -669,20 +658,16 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) ...@@ -669,20 +658,16 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
pwr = SDHCI_POWER_ON; pwr = SDHCI_POWER_ON;
switch (power) { switch (1 << power) {
case MMC_VDD_170: case MMC_VDD_165_195:
case MMC_VDD_180:
case MMC_VDD_190:
pwr |= SDHCI_POWER_180; pwr |= SDHCI_POWER_180;
break; break;
case MMC_VDD_290: case MMC_VDD_29_30:
case MMC_VDD_300: case MMC_VDD_30_31:
case MMC_VDD_310:
pwr |= SDHCI_POWER_300; pwr |= SDHCI_POWER_300;
break; break;
case MMC_VDD_320: case MMC_VDD_32_33:
case MMC_VDD_330: case MMC_VDD_33_34:
case MMC_VDD_340:
pwr |= SDHCI_POWER_330; pwr |= SDHCI_POWER_330;
break; break;
default: default:
...@@ -1294,7 +1279,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1294,7 +1279,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
if (caps & SDHCI_CAN_VDD_300) if (caps & SDHCI_CAN_VDD_300)
mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
if (caps & SDHCI_CAN_VDD_180) if (caps & SDHCI_CAN_VDD_180)
mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; mmc->ocr_avail |= MMC_VDD_165_195;
if (mmc->ocr_avail == 0) { if (mmc->ocr_avail == 0) {
printk(KERN_ERR "%s: Hardware doesn't report any " printk(KERN_ERR "%s: Hardware doesn't report any "
......
/* /*
* linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver
* *
* Copyright (C) 2005 Pierre Ossman, All Rights Reserved. * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -187,8 +187,6 @@ struct sdhci_host { ...@@ -187,8 +187,6 @@ struct sdhci_host {
int offset; /* Offset into current sg */ int offset; /* Offset into current sg */
int remain; /* Bytes left in current */ int remain; /* Bytes left in current */
int size; /* Remaining bytes in transfer */
char slot_descr[20]; /* Name for reservations */ char slot_descr[20]; /* Name for reservations */
int irq; /* Device IRQ */ int irq; /* Device IRQ */
......
...@@ -7,17 +7,19 @@ ...@@ -7,17 +7,19 @@
* 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/host.h> #include <linux/mmc/host.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/scatterlist.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;
...@@ -36,9 +38,9 @@ module_param(fixed_timeout, bool, 0644); ...@@ -36,9 +38,9 @@ module_param(fixed_timeout, bool, 0644);
#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 */
...@@ -48,8 +50,13 @@ module_param(fixed_timeout, bool, 0644); ...@@ -48,8 +50,13 @@ module_param(fixed_timeout, bool, 0644);
#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
...@@ -67,97 +74,255 @@ module_param(fixed_timeout, bool, 0644); ...@@ -67,97 +74,255 @@ module_param(fixed_timeout, bool, 0644);
#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 0x0800UL
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;
}
}
static 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;
}
dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos;
if (!dma_len) {
host->block_pos = 0;
host->sg_pos++;
if (host->sg_pos == host->sg_len)
return 1;
dma_len = sg_dma_len(&r_data->sg[host->sg_pos]);
}
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;
} }
...@@ -206,8 +371,10 @@ static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) ...@@ -206,8 +371,10 @@ static unsigned int tifm_sd_op_flags(struct mmc_command *cmd)
static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd)
{ {
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);
if (host->open_drain)
cmd_mask |= TIFM_MMCSD_ODTO;
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;
...@@ -232,191 +399,194 @@ static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) ...@@ -232,191 +399,194 @@ static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock)
| 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_card_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 */ if (host_status & TIFM_MMCSD_EOFB)
dev_dbg(&sock->dev, "setting dma for %d blocks\n", host->cmd_flags &= ~CARD_BUSY;
cmd->data->blocks); else if (host_status & TIFM_MMCSD_CB)
writel(TIFM_FIFO_INT_SETALL, host->cmd_flags |= CARD_BUSY;
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; tifm_sd_check_status(host);
writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS);
writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS);
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,
...@@ -452,146 +622,99 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -452,146 +622,99 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
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");
spin_unlock_irqrestore(&sock->lock, flags);
goto err_out;
}
host->written_blocks = 0;
host->flags &= ~CARD_BUSY;
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) if (host->no_dma) {
{ writel(TIFM_MMCSD_BUFINT
struct tifm_sd *host = (struct tifm_sd*)data; | readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
struct tifm_dev *sock = host->dev; sock->addr + SOCK_MMCSD_INT_ENABLE);
struct mmc_host *mmc = tifm_get_drvdata(sock); writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8)
struct mmc_request *mrq; | (TIFM_MMCSD_FIFO_SIZE - 1),
struct mmc_data *r_data = NULL; sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
unsigned long flags;
spin_lock_irqsave(&sock->lock, flags);
del_timer(&host->timer); host->sg_len = r_data->sg_len;
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;
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 - sg_init_one(&host->bounce_buf, host->bounce_buf_data,
readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; r_data->blksz);
r_data->bytes_xfered *= r_data->blksz;
r_data->bytes_xfered += r_data->blksz - if(1 != tifm_map_sg(sock, &host->bounce_buf, 1,
readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; r_data->flags & MMC_DATA_WRITE
} ? PCI_DMA_TODEVICE
tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, : PCI_DMA_FROMDEVICE)) {
(r_data->flags & MMC_DATA_WRITE) printk(KERN_ERR "%s : scatterlist map failed\n",
? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); sock->dev.bus_id);
} spin_unlock_irqrestore(&sock->lock, flags);
goto err_out;
writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), }
sock->addr + SOCK_CONTROL); host->sg_len = tifm_map_sg(sock, r_data->sg,
r_data->sg_len,
spin_unlock_irqrestore(&sock->lock, flags); r_data->flags
mmc_request_done(mmc, mrq); & MMC_DATA_WRITE
} ? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);
static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) if (host->sg_len < 1) {
{ printk(KERN_ERR "%s : scatterlist map failed\n",
struct tifm_sd *host = mmc_priv(mmc); sock->dev.bus_id);
struct tifm_dev *sock = host->dev; tifm_unmap_sg(sock, &host->bounce_buf, 1,
unsigned long flags; r_data->flags & MMC_DATA_WRITE
struct mmc_data *r_data = mrq->cmd->data; ? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);
spin_lock_irqsave(&sock->lock, flags); spin_unlock_irqrestore(&sock->lock, flags);
if (host->flags & EJECT) { goto err_out;
spin_unlock_irqrestore(&sock->lock, flags); }
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);
tifm_sd_set_dma_data(host, r_data);
}
host->buffer_size = mrq->cmd->data->blocks writel(r_data->blocks - 1,
* mrq->cmd->data->blksz; sock->addr + SOCK_MMCSD_NUM_BLOCKS);
writel(r_data->blksz - 1,
writel(TIFM_MMCSD_BUFINT sock->addr + SOCK_MMCSD_BLOCK_LEN);
| readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
sock->addr + SOCK_MMCSD_INT_ENABLE);
writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8)
| (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);
...@@ -603,7 +726,7 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -603,7 +726,7 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq)
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;
...@@ -617,68 +740,52 @@ static void tifm_sd_end_cmd_nodma(unsigned long data) ...@@ -617,68 +740,52 @@ static void tifm_sd_end_cmd_nodma(unsigned long data)
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);
} }
...@@ -691,8 +798,11 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -691,8 +798,11 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios)
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);
...@@ -737,41 +847,30 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -737,41 +847,30 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios)
& 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
...@@ -792,7 +891,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) ...@@ -792,7 +891,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
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;
...@@ -801,8 +900,8 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) ...@@ -801,8 +900,8 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
} }
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;
} }
...@@ -815,8 +914,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) ...@@ -815,8 +914,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
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)
...@@ -828,12 +926,14 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) ...@@ -828,12 +926,14 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
} }
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();
...@@ -848,7 +948,8 @@ static int tifm_sd_probe(struct tifm_dev *sock) ...@@ -848,7 +948,8 @@ static int tifm_sd_probe(struct tifm_dev *sock)
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;
} }
...@@ -857,41 +958,37 @@ static int tifm_sd_probe(struct tifm_dev *sock) ...@@ -857,41 +958,37 @@ static int tifm_sd_probe(struct tifm_dev *sock)
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); tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
tasklet_init(&host->finish_tasklet,
no_dma ? tifm_sd_end_cmd_nodma : 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_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE);
mmc->max_blk_count = 256; mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
// 2k maximum hw block length mmc->max_req_size = mmc->max_seg_size;
mmc->max_blk_size = 2048; mmc->max_phys_segs = mmc->max_hw_segs;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size; sock->card_event = tifm_sd_card_event;
sock->signal_irq = tifm_sd_signal_irq; 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;
} }
...@@ -900,19 +997,34 @@ static void tifm_sd_remove(struct tifm_dev *sock) ...@@ -900,19 +997,34 @@ static void tifm_sd_remove(struct tifm_dev *sock)
{ {
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;
spin_lock_irqsave(&sock->lock, flags);
host->eject = 1;
writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
mmiowb();
spin_unlock_irqrestore(&sock->lock, 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);
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);
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);
} }
...@@ -934,14 +1046,17 @@ static int tifm_sd_resume(struct tifm_dev *sock) ...@@ -934,14 +1046,17 @@ static int tifm_sd_resume(struct tifm_dev *sock)
{ {
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
...@@ -951,8 +1066,8 @@ static int tifm_sd_resume(struct tifm_dev *sock) ...@@ -951,8 +1066,8 @@ static int tifm_sd_resume(struct tifm_dev *sock)
#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 = {
......
/* /*
* linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
* *
* Copyright (C) 2004-2006 Pierre Ossman, All Rights Reserved. * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include <linux/pnp.h> #include <linux/pnp.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/dma.h> #include <asm/dma.h>
...@@ -178,9 +177,8 @@ static void wbsd_init_device(struct wbsd_host *host) ...@@ -178,9 +177,8 @@ static void wbsd_init_device(struct wbsd_host *host)
ier = 0; ier = 0;
ier |= WBSD_EINT_CARD; ier |= WBSD_EINT_CARD;
ier |= WBSD_EINT_FIFO_THRE; ier |= WBSD_EINT_FIFO_THRE;
ier |= WBSD_EINT_CCRC;
ier |= WBSD_EINT_TIMEOUT;
ier |= WBSD_EINT_CRC; ier |= WBSD_EINT_CRC;
ier |= WBSD_EINT_TIMEOUT;
ier |= WBSD_EINT_TC; ier |= WBSD_EINT_TC;
outb(ier, host->base + WBSD_EIR); outb(ier, host->base + WBSD_EIR);
...@@ -278,90 +276,36 @@ static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) ...@@ -278,90 +276,36 @@ static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
{ {
unsigned int len, i, size; unsigned int len, i;
struct scatterlist *sg; struct scatterlist *sg;
char *dmabuf = host->dma_buffer; char *dmabuf = host->dma_buffer;
char *sgbuf; char *sgbuf;
size = host->size;
sg = data->sg; sg = data->sg;
len = data->sg_len; len = data->sg_len;
/*
* Just loop through all entries. Size might not
* be the entire list though so make sure that
* we do not transfer too much.
*/
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
sgbuf = page_address(sg[i].page) + sg[i].offset; sgbuf = page_address(sg[i].page) + sg[i].offset;
if (size < sg[i].length) memcpy(dmabuf, sgbuf, sg[i].length);
memcpy(dmabuf, sgbuf, size);
else
memcpy(dmabuf, sgbuf, sg[i].length);
dmabuf += sg[i].length; dmabuf += sg[i].length;
if (size < sg[i].length)
size = 0;
else
size -= sg[i].length;
if (size == 0)
break;
} }
/*
* Check that we didn't get a request to transfer
* more data than can fit into the SG list.
*/
BUG_ON(size != 0);
host->size -= size;
} }
static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
{ {
unsigned int len, i, size; unsigned int len, i;
struct scatterlist *sg; struct scatterlist *sg;
char *dmabuf = host->dma_buffer; char *dmabuf = host->dma_buffer;
char *sgbuf; char *sgbuf;
size = host->size;
sg = data->sg; sg = data->sg;
len = data->sg_len; len = data->sg_len;
/*
* Just loop through all entries. Size might not
* be the entire list though so make sure that
* we do not transfer too much.
*/
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
sgbuf = page_address(sg[i].page) + sg[i].offset; sgbuf = page_address(sg[i].page) + sg[i].offset;
if (size < sg[i].length) memcpy(sgbuf, dmabuf, sg[i].length);
memcpy(sgbuf, dmabuf, size);
else
memcpy(sgbuf, dmabuf, sg[i].length);
dmabuf += sg[i].length; dmabuf += sg[i].length;
if (size < sg[i].length)
size = 0;
else
size -= sg[i].length;
if (size == 0)
break;
} }
/*
* Check that we didn't get a request to transfer
* more data than can fit into the SG list.
*/
BUG_ON(size != 0);
host->size -= size;
} }
/* /*
...@@ -484,7 +428,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) ...@@ -484,7 +428,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
/* /*
* Handle excessive data. * Handle excessive data.
*/ */
if (data->bytes_xfered == host->size) if (host->num_sg == 0)
return; return;
buffer = wbsd_sg_to_buffer(host) + host->offset; buffer = wbsd_sg_to_buffer(host) + host->offset;
...@@ -513,12 +457,6 @@ static void wbsd_empty_fifo(struct wbsd_host *host) ...@@ -513,12 +457,6 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
data->bytes_xfered++; data->bytes_xfered++;
/*
* Transfer done?
*/
if (data->bytes_xfered == host->size)
return;
/* /*
* End of scatter list entry? * End of scatter list entry?
*/ */
...@@ -526,19 +464,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host) ...@@ -526,19 +464,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
/* /*
* Get next entry. Check if last. * Get next entry. Check if last.
*/ */
if (!wbsd_next_sg(host)) { if (!wbsd_next_sg(host))
/*
* We should never reach this point.
* It means that we're trying to
* transfer more blocks than can fit
* into the scatter list.
*/
BUG_ON(1);
host->size = data->bytes_xfered;
return; return;
}
buffer = wbsd_sg_to_buffer(host); buffer = wbsd_sg_to_buffer(host);
} }
...@@ -550,7 +477,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) ...@@ -550,7 +477,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
* hardware problem. The chip doesn't trigger * hardware problem. The chip doesn't trigger
* FIFO threshold interrupts properly. * FIFO threshold interrupts properly.
*/ */
if ((host->size - data->bytes_xfered) < 16) if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
tasklet_schedule(&host->fifo_tasklet); tasklet_schedule(&host->fifo_tasklet);
} }
...@@ -564,7 +491,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) ...@@ -564,7 +491,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
* Check that we aren't being called after the * Check that we aren't being called after the
* entire buffer has been transfered. * entire buffer has been transfered.
*/ */
if (data->bytes_xfered == host->size) if (host->num_sg == 0)
return; return;
buffer = wbsd_sg_to_buffer(host) + host->offset; buffer = wbsd_sg_to_buffer(host) + host->offset;
...@@ -593,12 +520,6 @@ static void wbsd_fill_fifo(struct wbsd_host *host) ...@@ -593,12 +520,6 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
data->bytes_xfered++; data->bytes_xfered++;
/*
* Transfer done?
*/
if (data->bytes_xfered == host->size)
return;
/* /*
* End of scatter list entry? * End of scatter list entry?
*/ */
...@@ -606,19 +527,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host) ...@@ -606,19 +527,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
/* /*
* Get next entry. Check if last. * Get next entry. Check if last.
*/ */
if (!wbsd_next_sg(host)) { if (!wbsd_next_sg(host))
/*
* We should never reach this point.
* It means that we're trying to
* transfer more blocks than can fit
* into the scatter list.
*/
BUG_ON(1);
host->size = data->bytes_xfered;
return; return;
}
buffer = wbsd_sg_to_buffer(host); buffer = wbsd_sg_to_buffer(host);
} }
...@@ -638,6 +548,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -638,6 +548,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
u16 blksize; u16 blksize;
u8 setup; u8 setup;
unsigned long dmaflags; unsigned long dmaflags;
unsigned int size;
DBGF("blksz %04x blks %04x flags %08x\n", DBGF("blksz %04x blks %04x flags %08x\n",
data->blksz, data->blocks, data->flags); data->blksz, data->blocks, data->flags);
...@@ -647,7 +558,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -647,7 +558,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
/* /*
* Calculate size. * Calculate size.
*/ */
host->size = data->blocks * data->blksz; size = data->blocks * data->blksz;
/* /*
* Check timeout values for overflow. * Check timeout values for overflow.
...@@ -705,8 +616,8 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -705,8 +616,8 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
/* /*
* The buffer for DMA is only 64 kB. * The buffer for DMA is only 64 kB.
*/ */
BUG_ON(host->size > 0x10000); BUG_ON(size > 0x10000);
if (host->size > 0x10000) { if (size > 0x10000) {
data->error = MMC_ERR_INVALID; data->error = MMC_ERR_INVALID;
return; return;
} }
...@@ -729,7 +640,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -729,7 +640,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
else else
set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
set_dma_addr(host->dma, host->dma_addr); set_dma_addr(host->dma, host->dma_addr);
set_dma_count(host->dma, host->size); set_dma_count(host->dma, size);
enable_dma(host->dma); enable_dma(host->dma);
release_dma_lock(dmaflags); release_dma_lock(dmaflags);
...@@ -812,6 +723,10 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -812,6 +723,10 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
count = get_dma_residue(host->dma); count = get_dma_residue(host->dma);
release_dma_lock(dmaflags); release_dma_lock(dmaflags);
data->bytes_xfered = host->mrq->data->blocks *
host->mrq->data->blksz - count;
data->bytes_xfered -= data->bytes_xfered % data->blksz;
/* /*
* Any leftover data? * Any leftover data?
*/ */
...@@ -820,7 +735,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -820,7 +735,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
"%d bytes left.\n", "%d bytes left.\n",
mmc_hostname(host->mmc), count); mmc_hostname(host->mmc), count);
data->error = MMC_ERR_FAILED; if (data->error == MMC_ERR_NONE)
data->error = MMC_ERR_FAILED;
} else { } else {
/* /*
* Transfer data from DMA buffer to * Transfer data from DMA buffer to
...@@ -828,8 +744,11 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) ...@@ -828,8 +744,11 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
*/ */
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ)
wbsd_dma_to_sg(host, data); wbsd_dma_to_sg(host, data);
}
data->bytes_xfered = host->size; if (data->error != MMC_ERR_NONE) {
if (data->bytes_xfered)
data->bytes_xfered -= data->blksz;
} }
} }
...@@ -869,24 +788,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -869,24 +788,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
goto done; goto done;
} }
/*
* Does the request include data?
*/
if (cmd->data) { if (cmd->data) {
wbsd_prepare_data(host, cmd->data);
if (cmd->data->error != MMC_ERR_NONE)
goto done;
}
wbsd_send_command(host, cmd);
/*
* If this is a data transfer the request
* will be finished after the data has
* transfered.
*/
if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
/* /*
* The hardware is so delightfully stupid that it has a list * The hardware is so delightfully stupid that it has a list
* of "data" commands. If a command isn't on this list, it'll * of "data" commands. If a command isn't on this list, it'll
...@@ -918,14 +820,30 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -918,14 +820,30 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
"supported by this controller.\n", "supported by this controller.\n",
mmc_hostname(host->mmc), cmd->opcode); mmc_hostname(host->mmc), cmd->opcode);
#endif #endif
cmd->data->error = MMC_ERR_INVALID; cmd->error = MMC_ERR_INVALID;
if (cmd->data->stop)
wbsd_send_command(host, cmd->data->stop);
goto done; goto done;
}; };
}
/*
* Does the request include data?
*/
if (cmd->data) {
wbsd_prepare_data(host, cmd->data);
if (cmd->data->error != MMC_ERR_NONE)
goto done;
}
wbsd_send_command(host, cmd);
/*
* If this is a data transfer the request
* will be finished after the data has
* transfered.
*/
if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
/* /*
* Dirty fix for hardware bug. * Dirty fix for hardware bug.
*/ */
...@@ -1167,7 +1085,7 @@ static void wbsd_tasklet_fifo(unsigned long param) ...@@ -1167,7 +1085,7 @@ static void wbsd_tasklet_fifo(unsigned long param)
/* /*
* Done? * Done?
*/ */
if (host->size == data->bytes_xfered) { if (host->num_sg == 0) {
wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
} }
...@@ -1245,30 +1163,6 @@ static void wbsd_tasklet_finish(unsigned long param) ...@@ -1245,30 +1163,6 @@ static void wbsd_tasklet_finish(unsigned long param)
spin_unlock(&host->lock); spin_unlock(&host->lock);
} }
static void wbsd_tasklet_block(unsigned long param)
{
struct wbsd_host *host = (struct wbsd_host *)param;
struct mmc_data *data;
spin_lock(&host->lock);
if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
WBSD_CRC_OK) {
data = wbsd_get_data(host);
if (!data)
goto end;
DBGF("CRC error\n");
data->error = MMC_ERR_BADCRC;
tasklet_schedule(&host->finish_tasklet);
}
end:
spin_unlock(&host->lock);
}
/* /*
* Interrupt handling * Interrupt handling
*/ */
...@@ -1299,8 +1193,6 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id) ...@@ -1299,8 +1193,6 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
tasklet_hi_schedule(&host->crc_tasklet); tasklet_hi_schedule(&host->crc_tasklet);
if (isr & WBSD_INT_TIMEOUT) if (isr & WBSD_INT_TIMEOUT)
tasklet_hi_schedule(&host->timeout_tasklet); tasklet_hi_schedule(&host->timeout_tasklet);
if (isr & WBSD_INT_BUSYEND)
tasklet_hi_schedule(&host->block_tasklet);
if (isr & WBSD_INT_TC) if (isr & WBSD_INT_TC)
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
...@@ -1601,8 +1493,6 @@ static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) ...@@ -1601,8 +1493,6 @@ static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
(unsigned long)host); (unsigned long)host);
tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
(unsigned long)host); (unsigned long)host);
tasklet_init(&host->block_tasklet, wbsd_tasklet_block,
(unsigned long)host);
return 0; return 0;
} }
...@@ -1621,7 +1511,6 @@ static void __devexit wbsd_release_irq(struct wbsd_host *host) ...@@ -1621,7 +1511,6 @@ static void __devexit wbsd_release_irq(struct wbsd_host *host)
tasklet_kill(&host->crc_tasklet); tasklet_kill(&host->crc_tasklet);
tasklet_kill(&host->timeout_tasklet); tasklet_kill(&host->timeout_tasklet);
tasklet_kill(&host->finish_tasklet); tasklet_kill(&host->finish_tasklet);
tasklet_kill(&host->block_tasklet);
} }
/* /*
......
/* /*
* linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver
* *
* Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved. * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -46,10 +46,10 @@ ...@@ -46,10 +46,10 @@
#define WBSD_EINT_CARD 0x40 #define WBSD_EINT_CARD 0x40
#define WBSD_EINT_FIFO_THRE 0x20 #define WBSD_EINT_FIFO_THRE 0x20
#define WBSD_EINT_CCRC 0x10 #define WBSD_EINT_CRC 0x10
#define WBSD_EINT_TIMEOUT 0x08 #define WBSD_EINT_TIMEOUT 0x08
#define WBSD_EINT_PROGEND 0x04 #define WBSD_EINT_PROGEND 0x04
#define WBSD_EINT_CRC 0x02 #define WBSD_EINT_BUSYEND 0x02
#define WBSD_EINT_TC 0x01 #define WBSD_EINT_TC 0x01
#define WBSD_INT_PENDING 0x80 #define WBSD_INT_PENDING 0x80
...@@ -158,8 +158,6 @@ struct wbsd_host ...@@ -158,8 +158,6 @@ struct wbsd_host
unsigned int offset; /* Offset into current entry */ unsigned int offset; /* Offset into current entry */
unsigned int remain; /* Data left in curren entry */ unsigned int remain; /* Data left in curren entry */
int size; /* Total size of transfer */
char* dma_buffer; /* ISA DMA buffer */ char* dma_buffer; /* ISA DMA buffer */
dma_addr_t dma_addr; /* Physical address for same */ dma_addr_t dma_addr; /* Physical address for same */
...@@ -182,7 +180,6 @@ struct wbsd_host ...@@ -182,7 +180,6 @@ struct wbsd_host
struct tasklet_struct crc_tasklet; struct tasklet_struct crc_tasklet;
struct tasklet_struct timeout_tasklet; struct tasklet_struct timeout_tasklet;
struct tasklet_struct finish_tasklet; struct tasklet_struct finish_tasklet;
struct tasklet_struct block_tasklet;
struct timer_list ignore_timer; /* Ignore detection timer */ struct timer_list ignore_timer; /* Ignore detection timer */
}; };
/*
* linux/drivers/mmc/mmc.c
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
* SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/pagemap.h>
#include <linux/err.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include "mmc.h"
#define CMD_RETRIES 3
/*
* OCR Bit positions to 10s of Vdd mV.
*/
static const unsigned short mmc_ocr_bit_to_vdd[] = {
150, 155, 160, 165, 170, 180, 190, 200,
210, 220, 230, 240, 250, 260, 270, 280,
290, 300, 310, 320, 330, 340, 350, 360
};
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
};
static const unsigned char tran_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static const unsigned int tacc_exp[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static const unsigned int tacc_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
/**
* mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request
* @mrq: MMC request which request
*
* MMC drivers should call this function when they have completed
* their processing of a request.
*/
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n",
mmc_hostname(host), cmd->opcode, err,
mrq->data ? mrq->data->error : 0,
mrq->stop ? mrq->stop->error : 0,
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
if (err && cmd->retries) {
cmd->retries--;
cmd->error = 0;
host->ops->request(host, mrq);
} else if (mrq->done) {
mrq->done(mrq);
}
}
EXPORT_SYMBOL(mmc_request_done);
/**
* mmc_start_request - start a command on a host
* @host: MMC host to start command on
* @mrq: MMC request to start
*
* Queue a command on the specified host. We expect the
* caller to be holding the host lock with interrupts disabled.
*/
void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->cmd->opcode,
mrq->cmd->arg, mrq->cmd->flags);
WARN_ON(!host->claimed);
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
BUG_ON(mrq->data->blksz > host->max_blk_size);
BUG_ON(mrq->data->blocks > host->max_blk_count);
BUG_ON(mrq->data->blocks * mrq->data->blksz >
host->max_req_size);
mrq->cmd->data = mrq->data;
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
}
host->ops->request(host, mrq);
}
EXPORT_SYMBOL(mmc_start_request);
static void mmc_wait_done(struct mmc_request *mrq)
{
complete(mrq->done_data);
}
int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
DECLARE_COMPLETION_ONSTACK(complete);
mrq->done_data = &complete;
mrq->done = mmc_wait_done;
mmc_start_request(host, mrq);
wait_for_completion(&complete);
return 0;
}
EXPORT_SYMBOL(mmc_wait_for_req);
/**
* mmc_wait_for_cmd - start a command and wait for completion
* @host: MMC host to start command
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Start a new MMC command for a host, and wait for the command
* to complete. Return any error that occurred while the command
* was executing. Do not attempt to parse the response.
*/
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
BUG_ON(!host->claimed);
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = retries;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
return cmd->error;
}
EXPORT_SYMBOL(mmc_wait_for_cmd);
/**
* mmc_wait_for_app_cmd - start an application command and wait for
completion
* @host: MMC host to start command
* @rca: RCA to send MMC_APP_CMD to
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Sends a MMC_APP_CMD, checks the card response, sends the command
* in the parameter and waits for it to complete. Return any error
* that occurred while the command was executing. Do not attempt to
* parse the response.
*/
int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca,
struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
struct mmc_command appcmd;
int i, err;
BUG_ON(!host->claimed);
BUG_ON(retries < 0);
err = MMC_ERR_INVALID;
/*
* We have to resend MMC_APP_CMD for each attempt so
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
memset(&mrq, 0, sizeof(struct mmc_request));
appcmd.opcode = MMC_APP_CMD;
appcmd.arg = rca << 16;
appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
appcmd.retries = 0;
memset(appcmd.resp, 0, sizeof(appcmd.resp));
appcmd.data = NULL;
mrq.cmd = &appcmd;
appcmd.data = NULL;
mmc_wait_for_req(host, &mrq);
if (appcmd.error) {
err = appcmd.error;
continue;
}
/* Check that card supported application commands */
if (!(appcmd.resp[0] & R1_APP_CMD))
return MMC_ERR_FAILED;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = 0;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
err = cmd->error;
if (cmd->error == MMC_ERR_NONE)
break;
}
return err;
}
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
/**
* mmc_set_data_timeout - set the timeout for a data command
* @data: data phase for command
* @card: the MMC card associated with the data transfer
* @write: flag to differentiate reads from writes
*/
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
int write)
{
unsigned int mult;
/*
* SD cards use a 100 multiplier rather than 10
*/
mult = mmc_card_sd(card) ? 100 : 10;
/*
* Scale up the multiplier (and therefore the timeout) by
* the r2w factor for writes.
*/
if (write)
mult <<= card->csd.r2w_factor;
data->timeout_ns = card->csd.tacc_ns * mult;
data->timeout_clks = card->csd.tacc_clks * mult;
/*
* SD cards also have an upper limit on the timeout.
*/
if (mmc_card_sd(card)) {
unsigned int timeout_us, limit_us;
timeout_us = data->timeout_ns / 1000;
timeout_us += data->timeout_clks * 1000 /
(card->host->ios.clock / 1000);
if (write)
limit_us = 250000;
else
limit_us = 100000;
/*
* SDHC cards always use these fixed values.
*/
if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
data->timeout_ns = limit_us * 1000;
data->timeout_clks = 0;
}
}
}
EXPORT_SYMBOL(mmc_set_data_timeout);
static int mmc_select_card(struct mmc_host *host, struct mmc_card *card);
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @card: mmc card to claim host for
*
* Claim a host for a set of operations. If a valid card
* is passed and this wasn't the last card selected, select
* the card before returning.
*
* Note: you should use mmc_card_claim_host or mmc_claim_host.
*/
int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int err = 0;
add_wait_queue(&host->wq, &wait);
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!host->claimed)
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);
host->claimed = 1;
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
if (card != (void *)-1) {
err = mmc_select_card(host, card);
if (err != MMC_ERR_NONE)
return err;
}
return err;
}
EXPORT_SYMBOL(__mmc_claim_host);
/**
* mmc_release_host - release a host
* @host: mmc host to release
*
* Release a MMC host, allowing others to claim the host
* for their operations.
*/
void mmc_release_host(struct mmc_host *host)
{
unsigned long flags;
BUG_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags);
host->claimed = 0;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);
}
EXPORT_SYMBOL(mmc_release_host);
static inline void mmc_set_ios(struct mmc_host *host)
{
struct mmc_ios *ios = &host->ios;
pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
"width %u timing %u\n",
mmc_hostname(host), ios->clock, ios->bus_mode,
ios->power_mode, ios->chip_select, ios->vdd,
ios->bus_width, ios->timing);
host->ops->set_ios(host, ios);
}
static int mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
int err;
struct mmc_command cmd;
BUG_ON(!host->claimed);
if (host->card_selected == card)
return MMC_ERR_NONE;
host->card_selected = card;
cmd.opcode = MMC_SELECT_CARD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
/*
* We can only change the bus width of SD cards when
* they are selected so we have to put the handling
* here.
*
* The card is in 1 bit mode by default so
* we only need to change if it supports the
* wider version.
*/
if (mmc_card_sd(card) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
/*
* Default bus width is 1 bit.
*/
host->ios.bus_width = MMC_BUS_WIDTH_1;
if (host->caps & MMC_CAP_4_BIT_DATA) {
struct mmc_command cmd;
cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.arg = SD_BUS_WIDTH_4;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_app_cmd(host, card->rca, &cmd,
CMD_RETRIES);
if (err != MMC_ERR_NONE)
return err;
host->ios.bus_width = MMC_BUS_WIDTH_4;
}
}
mmc_set_ios(host);
return MMC_ERR_NONE;
}
/*
* Ensure that no card is selected.
*/
static void mmc_deselect_cards(struct mmc_host *host)
{
struct mmc_command cmd;
if (host->card_selected) {
host->card_selected = NULL;
cmd.opcode = MMC_SELECT_CARD;
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
mmc_wait_for_cmd(host, &cmd, 0);
}
}
static inline void mmc_delay(unsigned int ms)
{
if (ms < 1000 / HZ) {
cond_resched();
mdelay(ms);
} else {
msleep(ms);
}
}
/*
* Mask off any voltages we don't support and select
* the lowest voltage
*/
static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
int bit;
ocr &= host->ocr_avail;
bit = ffs(ocr);
if (bit) {
bit -= 1;
ocr &= 3 << bit;
host->ios.vdd = bit;
mmc_set_ios(host);
} else {
ocr = 0;
}
return ocr;
}
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
const int __off = 3 - ((start) / 32); \
const int __shft = (start) & 31; \
u32 __res; \
\
__res = resp[__off] >> __shft; \
if (__size + __shft > 32) \
__res |= resp[__off-1] << ((32 - __shft) % 32); \
__res & __mask; \
})
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
static void mmc_decode_cid(struct mmc_card *card)
{
u32 *resp = card->raw_cid;
memset(&card->cid, 0, sizeof(struct mmc_cid));
if (mmc_card_sd(card)) {
/*
* SD doesn't currently have a version field so we will
* have to assume we can parse this.
*/
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4);
card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4);
card->cid.serial = UNSTUFF_BITS(resp, 24, 32);
card->cid.year = UNSTUFF_BITS(resp, 12, 8);
card->cid.month = UNSTUFF_BITS(resp, 8, 4);
card->cid.year += 2000; /* SD cards year offset */
} else {
/*
* The selection of the format here is based upon published
* specs from sandisk and from what people have reported.
*/
switch (card->csd.mmca_vsn) {
case 0: /* MMC v1.0 - v1.2 */
case 1: /* MMC v1.4 */
card->cid.manfid = UNSTUFF_BITS(resp, 104, 24);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8);
card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4);
card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4);
card->cid.serial = UNSTUFF_BITS(resp, 16, 24);
card->cid.month = UNSTUFF_BITS(resp, 12, 4);
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
break;
case 2: /* MMC v2.0 - v2.2 */
case 3: /* MMC v3.1 - v3.3 */
case 4: /* MMC v4 */
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
card->cid.month = UNSTUFF_BITS(resp, 12, 4);
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
break;
default:
printk("%s: card has unknown MMCA version %d\n",
mmc_hostname(card->host), card->csd.mmca_vsn);
mmc_card_set_bad(card);
break;
}
}
}
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
static void mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
unsigned int e, m, csd_struct;
u32 *resp = card->raw_csd;
if (mmc_card_sd(card)) {
csd_struct = UNSTUFF_BITS(resp, 126, 2);
switch (csd_struct) {
case 0:
m = UNSTUFF_BITS(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3);
csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
m = UNSTUFF_BITS(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
e = UNSTUFF_BITS(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
break;
case 1:
/*
* This is a block-addressed SDHC card. Most
* interesting fields are unused and have fixed
* values. To avoid getting tripped by buggy cards,
* we assume those fixed values ourselves.
*/
mmc_card_set_blockaddr(card);
csd->tacc_ns = 0; /* Unused */
csd->tacc_clks = 0; /* Unused */
m = UNSTUFF_BITS(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
m = UNSTUFF_BITS(resp, 48, 22);
csd->capacity = (1 + m) << 10;
csd->read_blkbits = 9;
csd->read_partial = 0;
csd->write_misalign = 0;
csd->read_misalign = 0;
csd->r2w_factor = 4; /* Unused */
csd->write_blkbits = 9;
csd->write_partial = 0;
break;
default:
printk("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct);
mmc_card_set_bad(card);
return;
}
} else {
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
*/
csd_struct = UNSTUFF_BITS(resp, 126, 2);
if (csd_struct != 1 && csd_struct != 2) {
printk("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct);
mmc_card_set_bad(card);
return;
}
csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4);
m = UNSTUFF_BITS(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3);
csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
m = UNSTUFF_BITS(resp, 99, 4);
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
e = UNSTUFF_BITS(resp, 47, 3);
m = UNSTUFF_BITS(resp, 62, 12);
csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
}
}
/*
* Given a 64-bit response, decode to our card SCR structure.
*/
static void mmc_decode_scr(struct mmc_card *card)
{
struct sd_scr *scr = &card->scr;
unsigned int scr_struct;
u32 resp[4];
BUG_ON(!mmc_card_sd(card));
resp[3] = card->raw_scr[1];
resp[2] = card->raw_scr[0];
scr_struct = UNSTUFF_BITS(resp, 60, 4);
if (scr_struct != 0) {
printk("%s: unrecognised SCR structure version %d\n",
mmc_hostname(card->host), scr_struct);
mmc_card_set_bad(card);
return;
}
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
}
/*
* Locate a MMC card on this MMC host given a raw CID.
*/
static struct mmc_card *mmc_find_card(struct mmc_host *host, u32 *raw_cid)
{
struct mmc_card *card;
list_for_each_entry(card, &host->cards, node) {
if (memcmp(card->raw_cid, raw_cid, sizeof(card->raw_cid)) == 0)
return card;
}
return NULL;
}
/*
* Allocate a new MMC card, and assign a unique RCA.
*/
static struct mmc_card *
mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
{
struct mmc_card *card, *c;
unsigned int rca = *frca;
card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
mmc_init_card(card, host);
memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid));
again:
list_for_each_entry(c, &host->cards, node)
if (c->rca == rca) {
rca++;
goto again;
}
card->rca = rca;
*frca = rca;
return card;
}
/*
* Tell attached cards to go to IDLE state
*/
static void mmc_idle_cards(struct mmc_host *host)
{
struct mmc_command cmd;
host->ios.chip_select = MMC_CS_HIGH;
mmc_set_ios(host);
mmc_delay(1);
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1);
host->ios.chip_select = MMC_CS_DONTCARE;
mmc_set_ios(host);
mmc_delay(1);
}
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
* We then wait a bit for the power to stabilise. Finally,
* enable the bus drivers and clock to the card.
*
* We must _NOT_ enable the clock prior to power stablising.
*
* If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage.
*/
static void mmc_power_up(struct mmc_host *host)
{
int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
mmc_delay(1);
host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
mmc_delay(2);
}
static void mmc_power_off(struct mmc_host *host)
{
host->ios.clock = 0;
host->ios.vdd = 0;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.power_mode = MMC_POWER_OFF;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
}
static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
cmd.opcode = SD_APP_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[0];
return err;
}
static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2)
{
struct mmc_command cmd;
int err, sd2;
static const u8 test_pattern = 0xAA;
/*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
* before SD_APP_OP_COND. This command will harmlessly fail for
* SD 1.0 cards.
*/
cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err == MMC_ERR_NONE) {
if ((cmd.resp[0] & 0xFF) == test_pattern) {
sd2 = 1;
} else {
sd2 = 0;
err = MMC_ERR_FAILED;
}
} else {
/*
* Treat errors as SD 1.0 card.
*/
sd2 = 0;
err = MMC_ERR_NONE;
}
if (rsd2)
*rsd2 = sd2;
return err;
}
/*
* Discover cards by requesting their CID. If this command
* times out, it is not an error; there are no further cards
* to be discovered. Add new cards to the list.
*
* Create a mmc_card entry for each discovered card, assigning
* it an RCA, and save the raw CID for decoding later.
*/
static void mmc_discover_cards(struct mmc_host *host)
{
struct mmc_card *card;
unsigned int first_rca = 1, err;
while (1) {
struct mmc_command cmd;
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err == MMC_ERR_TIMEOUT) {
err = MMC_ERR_NONE;
break;
}
if (err != MMC_ERR_NONE) {
printk(KERN_ERR "%s: error requesting CID: %d\n",
mmc_hostname(host), err);
break;
}
card = mmc_find_card(host, cmd.resp);
if (!card) {
card = mmc_alloc_card(host, cmd.resp, &first_rca);
if (IS_ERR(card)) {
err = PTR_ERR(card);
break;
}
list_add(&card->node, &host->cards);
}
card->state &= ~MMC_STATE_DEAD;
if (host->mode == MMC_MODE_SD) {
mmc_card_set_sd(card);
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
mmc_card_set_dead(card);
else {
card->rca = cmd.resp[0] >> 16;
if (!host->ops->get_ro) {
printk(KERN_WARNING "%s: host does not "
"support reading read-only "
"switch. assuming write-enable.\n",
mmc_hostname(host));
} else {
if (host->ops->get_ro(host))
mmc_card_set_readonly(card);
}
}
} else {
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
mmc_card_set_dead(card);
}
}
}
static void mmc_read_csds(struct mmc_host *host)
{
struct mmc_card *card;
list_for_each_entry(card, &host->cards, node) {
struct mmc_command cmd;
int err;
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
continue;
cmd.opcode = MMC_SEND_CSD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE) {
mmc_card_set_dead(card);
continue;
}
memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd));
mmc_decode_csd(card);
mmc_decode_cid(card);
}
}
static void mmc_process_ext_csds(struct mmc_host *host)
{
int err;
struct mmc_card *card;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
/*
* As the ext_csd is so large and mostly unused, we don't store the
* raw block in mmc_card.
*/
u8 *ext_csd;
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) {
printk("%s: could not allocate a buffer to receive the ext_csd."
"mmc v4 cards will be treated as v3.\n",
mmc_hostname(host));
return;
}
list_for_each_entry(card, &host->cards, node) {
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
continue;
if (mmc_card_sd(card))
continue;
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
continue;
err = mmc_select_card(host, card);
if (err != MMC_ERR_NONE) {
mmc_card_set_dead(card);
continue;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, card, 0);
data.blksz = 512;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, ext_csd, 512);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
printk("%s: unable to read EXT_CSD, performance "
"might suffer.\n", mmc_hostname(card->host));
continue;
}
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
break;
case EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 26000000;
break;
default:
/* MMC v4 spec says this cannot happen */
printk("%s: card is mmc v4 but doesn't support "
"any high-speed modes.\n",
mmc_hostname(card->host));
continue;
}
if (host->caps & MMC_CAP_MMC_HIGHSPEED) {
/* Activate highspeed support. */
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_HS_TIMING << 16) |
(1 << 8) |
EXT_CSD_CMD_SET_NORMAL;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE) {
printk("%s: failed to switch card to mmc v4 "
"high-speed mode.\n",
mmc_hostname(card->host));
continue;
}
mmc_card_set_highspeed(card);
host->ios.timing = MMC_TIMING_SD_HS;
mmc_set_ios(host);
}
/* Check for host support for wide-bus modes. */
if (host->caps & MMC_CAP_4_BIT_DATA) {
/* Activate 4-bit support. */
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_BUS_WIDTH << 16) |
(EXT_CSD_BUS_WIDTH_4 << 8) |
EXT_CSD_CMD_SET_NORMAL;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE) {
printk("%s: failed to switch card to "
"mmc v4 4-bit bus mode.\n",
mmc_hostname(card->host));
continue;
}
host->ios.bus_width = MMC_BUS_WIDTH_4;
mmc_set_ios(host);
}
}
kfree(ext_csd);
mmc_deselect_cards(host);
}
static void mmc_read_scrs(struct mmc_host *host)
{
int err;
struct mmc_card *card;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
list_for_each_entry(card, &host->cards, node) {
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
continue;
if (!mmc_card_sd(card))
continue;
err = mmc_select_card(host, card);
if (err != MMC_ERR_NONE) {
mmc_card_set_dead(card);
continue;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_APP_CMD;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) {
mmc_card_set_dead(card);
continue;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, card, 0);
data.blksz = 1 << 3;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, (u8*)card->raw_scr, 8);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
mmc_card_set_dead(card);
continue;
}
card->raw_scr[0] = ntohl(card->raw_scr[0]);
card->raw_scr[1] = ntohl(card->raw_scr[1]);
mmc_decode_scr(card);
}
mmc_deselect_cards(host);
}
static void mmc_read_switch_caps(struct mmc_host *host)
{
int err;
struct mmc_card *card;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
unsigned char *status;
struct scatterlist sg;
if (!(host->caps & MMC_CAP_SD_HIGHSPEED))
return;
status = kmalloc(64, GFP_KERNEL);
if (!status) {
printk(KERN_WARNING "%s: Unable to allocate buffer for "
"reading switch capabilities.\n",
mmc_hostname(host));
return;
}
list_for_each_entry(card, &host->cards, node) {
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
continue;
if (!mmc_card_sd(card))
continue;
if (card->scr.sda_vsn < SCR_SPEC_VER_1)
continue;
err = mmc_select_card(host, card);
if (err != MMC_ERR_NONE) {
mmc_card_set_dead(card);
continue;
}
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SWITCH;
cmd.arg = 0x00FFFFF1;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, card, 0);
data.blksz = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, status, 64);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
printk("%s: unable to read switch capabilities, "
"performance might suffer.\n",
mmc_hostname(card->host));
continue;
}
if (status[13] & 0x02)
card->sw_caps.hs_max_dtr = 50000000;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SWITCH;
cmd.arg = 0x80FFFFF1;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data));
mmc_set_data_timeout(&data, card, 0);
data.blksz = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
sg_init_one(&sg, status, 64);
mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE ||
(status[16] & 0xF) != 1) {
printk(KERN_WARNING "%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(host));
continue;
}
mmc_card_set_highspeed(card);
host->ios.timing = MMC_TIMING_SD_HS;
mmc_set_ios(host);
}
kfree(status);
mmc_deselect_cards(host);
}
static unsigned int mmc_calculate_clock(struct mmc_host *host)
{
struct mmc_card *card;
unsigned int max_dtr = host->f_max;
list_for_each_entry(card, &host->cards, node)
if (!mmc_card_dead(card)) {
if (mmc_card_highspeed(card) && mmc_card_sd(card)) {
if (max_dtr > card->sw_caps.hs_max_dtr)
max_dtr = card->sw_caps.hs_max_dtr;
} else if (mmc_card_highspeed(card) && !mmc_card_sd(card)) {
if (max_dtr > card->ext_csd.hs_max_dtr)
max_dtr = card->ext_csd.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
max_dtr = card->csd.max_dtr;
}
}
pr_debug("%s: selected %d.%03dMHz transfer rate\n",
mmc_hostname(host),
max_dtr / 1000000, (max_dtr / 1000) % 1000);
return max_dtr;
}
/*
* Check whether cards we already know about are still present.
* We do this by requesting status, and checking whether a card
* responds.
*
* A request for status does not cause a state change in data
* transfer mode.
*/
static void mmc_check_cards(struct mmc_host *host)
{
struct list_head *l, *n;
mmc_deselect_cards(host);
list_for_each_safe(l, n, &host->cards) {
struct mmc_card *card = mmc_list_to_card(l);
struct mmc_command cmd;
int err;
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err == MMC_ERR_NONE)
continue;
mmc_card_set_dead(card);
}
}
static void mmc_setup(struct mmc_host *host)
{
if (host->ios.power_mode != MMC_POWER_ON) {
int err;
u32 ocr;
host->mode = MMC_MODE_SD;
mmc_power_up(host);
mmc_idle_cards(host);
err = mmc_send_if_cond(host, host->ocr_avail, NULL);
if (err != MMC_ERR_NONE) {
return;
}
err = mmc_send_app_op_cond(host, 0, &ocr);
/*
* If we fail to detect any SD cards then try
* searching for MMC cards.
*/
if (err != MMC_ERR_NONE) {
host->mode = MMC_MODE_MMC;
err = mmc_send_op_cond(host, 0, &ocr);
if (err != MMC_ERR_NONE)
return;
}
host->ocr = mmc_select_voltage(host, ocr);
/*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to
* respond.
*/
if (host->ocr)
mmc_idle_cards(host);
} else {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.clock = host->f_min;
mmc_set_ios(host);
/*
* We should remember the OCR mask from the existing
* cards, and detect the new cards OCR mask, combine
* the two and re-select the VDD. However, if we do
* change VDD, we should do an idle, and then do a
* full re-initialisation. We would need to notify
* drivers so that they can re-setup the cards as
* well, while keeping their queues at bay.
*
* For the moment, we take the easy way out - if the
* new cards don't like our currently selected VDD,
* they drop off the bus.
*/
}
if (host->ocr == 0)
return;
/*
* Send the selected OCR multiple times... until the cards
* all get the idea that they should be ready for CMD2.
* (My SanDisk card seems to need this.)
*/
if (host->mode == MMC_MODE_SD) {
int err, sd2;
err = mmc_send_if_cond(host, host->ocr, &sd2);
if (err == MMC_ERR_NONE) {
/*
* If SD_SEND_IF_COND indicates an SD 2.0
* compliant card and we should set bit 30
* of the ocr to indicate that we can handle
* block-addressed SDHC cards.
*/
mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL);
}
} else {
mmc_send_op_cond(host, host->ocr, NULL);
}
mmc_discover_cards(host);
/*
* Ok, now switch to push-pull mode.
*/
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
mmc_set_ios(host);
mmc_read_csds(host);
if (host->mode == MMC_MODE_SD) {
mmc_read_scrs(host);
mmc_read_switch_caps(host);
} else
mmc_process_ext_csds(host);
}
/**
* mmc_detect_change - process change of state on a MMC socket
* @host: host which changed state.
* @delay: optional delay to wait before detection (jiffies)
*
* All we know is that card(s) have been inserted or removed
* from the socket(s). We don't know which socket or cards.
*/
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
mmc_schedule_delayed_work(&host->detect, delay);
}
EXPORT_SYMBOL(mmc_detect_change);
static void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
struct list_head *l, *n;
unsigned char power_mode;
mmc_claim_host(host);
/*
* Check for removed cards and newly inserted ones. We check for
* removed cards first so we can intelligently re-select the VDD.
*/
power_mode = host->ios.power_mode;
if (power_mode == MMC_POWER_ON)
mmc_check_cards(host);
mmc_setup(host);
/*
* Some broken cards process CMD1 even in stand-by state. There is
* no reply, but an ILLEGAL_COMMAND error is cached and returned
* after next command. We poll for card status here to clear any
* possibly pending error.
*/
if (power_mode == MMC_POWER_ON)
mmc_check_cards(host);
if (!list_empty(&host->cards)) {
/*
* (Re-)calculate the fastest clock rate which the
* attached cards and the host support.
*/
host->ios.clock = mmc_calculate_clock(host);
mmc_set_ios(host);
}
mmc_release_host(host);
list_for_each_safe(l, n, &host->cards) {
struct mmc_card *card = mmc_list_to_card(l);
/*
* If this is a new and good card, register it.
*/
if (!mmc_card_present(card) && !mmc_card_dead(card)) {
if (mmc_register_card(card))
mmc_card_set_dead(card);
else
mmc_card_set_present(card);
}
/*
* If this card is dead, destroy it.
*/
if (mmc_card_dead(card)) {
list_del(&card->node);
mmc_remove_card(card);
}
}
/*
* If we discover that there are no cards on the
* bus, turn off the clock and power down.
*/
if (list_empty(&host->cards))
mmc_power_off(host);
}
/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
* @dev: pointer to host device model structure
*
* Initialise the per-host structure.
*/
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
struct mmc_host *host;
host = mmc_alloc_host_sysfs(extra, dev);
if (host) {
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_LIST_HEAD(&host->cards);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
*/
host->max_hw_segs = 1;
host->max_phys_segs = 1;
host->max_seg_size = PAGE_CACHE_SIZE;
host->max_req_size = PAGE_CACHE_SIZE;
host->max_blk_size = 512;
host->max_blk_count = PAGE_CACHE_SIZE / 512;
}
return host;
}
EXPORT_SYMBOL(mmc_alloc_host);
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
*/
int mmc_add_host(struct mmc_host *host)
{
int ret;
ret = mmc_add_host_sysfs(host);
if (ret == 0) {
mmc_power_off(host);
mmc_detect_change(host, 0);
}
return ret;
}
EXPORT_SYMBOL(mmc_add_host);
/**
* mmc_remove_host - remove host hardware
* @host: mmc host
*
* Unregister and remove all cards associated with this host,
* and power down the MMC bus.
*/
void mmc_remove_host(struct mmc_host *host)
{
struct list_head *l, *n;
list_for_each_safe(l, n, &host->cards) {
struct mmc_card *card = mmc_list_to_card(l);
mmc_remove_card(card);
}
mmc_power_off(host);
mmc_remove_host_sysfs(host);
}
EXPORT_SYMBOL(mmc_remove_host);
/**
* mmc_free_host - free the host structure
* @host: mmc host
*
* Free the host once all references to it have been dropped.
*/
void mmc_free_host(struct mmc_host *host)
{
mmc_flush_scheduled_work();
mmc_free_host_sysfs(host);
}
EXPORT_SYMBOL(mmc_free_host);
#ifdef CONFIG_PM
/**
* mmc_suspend_host - suspend a host
* @host: mmc host
* @state: suspend mode (PM_SUSPEND_xxx)
*/
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
{
mmc_claim_host(host);
mmc_deselect_cards(host);
mmc_power_off(host);
mmc_release_host(host);
return 0;
}
EXPORT_SYMBOL(mmc_suspend_host);
/**
* mmc_resume_host - resume a previously suspended host
* @host: mmc host
*/
int mmc_resume_host(struct mmc_host *host)
{
mmc_rescan(&host->detect.work);
return 0;
}
EXPORT_SYMBOL(mmc_resume_host);
#endif
MODULE_LICENSE("GPL");
#ifndef ASMARM_ARCH_MMC_H #ifndef ASMARM_ARCH_MMC_H
#define ASMARM_ARCH_MMC_H #define ASMARM_ARCH_MMC_H
#include <linux/mmc/protocol.h> #include <linux/mmc/host.h>
struct imxmmc_platform_data { struct imxmmc_platform_data {
int (*card_present)(void); int (*card_present)(void);
......
#ifndef ASMARM_ARCH_MMC_H #ifndef ASMARM_ARCH_MMC_H
#define ASMARM_ARCH_MMC_H #define ASMARM_ARCH_MMC_H
#include <linux/mmc/protocol.h> #include <linux/mmc/host.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
struct device; struct device;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#ifndef ASMARM_MACH_MMC_H #ifndef ASMARM_MACH_MMC_H
#define ASMARM_MACH_MMC_H #define ASMARM_MACH_MMC_H
#include <linux/mmc/protocol.h> #include <linux/mmc/host.h>
struct mmc_platform_data { struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */ unsigned int ocr_mask; /* available voltages */
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#ifndef LINUX_MMC_CARD_H #ifndef LINUX_MMC_CARD_H
#define LINUX_MMC_CARD_H #define LINUX_MMC_CARD_H
#include <linux/mmc/mmc.h> #include <linux/mmc/core.h>
struct mmc_cid { struct mmc_cid {
unsigned int manfid; unsigned int manfid;
...@@ -41,6 +41,7 @@ struct mmc_csd { ...@@ -41,6 +41,7 @@ struct mmc_csd {
struct mmc_ext_csd { struct mmc_ext_csd {
unsigned int hs_max_dtr; unsigned int hs_max_dtr;
unsigned int sectors;
}; };
struct sd_scr { struct sd_scr {
...@@ -60,18 +61,17 @@ struct mmc_host; ...@@ -60,18 +61,17 @@ struct mmc_host;
* MMC device * MMC device
*/ */
struct mmc_card { struct mmc_card {
struct list_head node; /* node in hosts devices list */
struct mmc_host *host; /* the host this device belongs to */ struct mmc_host *host; /* the host this device belongs to */
struct device dev; /* the device */ struct device dev; /* the device */
unsigned int rca; /* relative card address of device */ unsigned int rca; /* relative card address of device */
unsigned int type; /* card type */
#define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD card */
unsigned int state; /* (our) card state */ unsigned int state; /* (our) card state */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */
#define MMC_STATE_BAD (1<<2) /* unrecognised device */ #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
#define MMC_STATE_SDCARD (1<<3) /* is an SD card */ #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
#define MMC_STATE_READONLY (1<<4) /* card is read-only */
#define MMC_STATE_HIGHSPEED (1<<5) /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<6) /* card uses block-addressing */
u32 raw_cid[4]; /* raw card CID */ u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */ u32 raw_csd[4]; /* raw card CSD */
u32 raw_scr[2]; /* raw card SCR */ u32 raw_scr[2]; /* raw card SCR */
...@@ -82,18 +82,15 @@ struct mmc_card { ...@@ -82,18 +82,15 @@ struct mmc_card {
struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ struct sd_switch_caps sw_caps; /* switch (CMD6) caps */
}; };
#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD)
#define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD)
#define mmc_card_sd(c) ((c)->state & MMC_STATE_SDCARD)
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD)
#define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD)
#define mmc_card_set_sd(c) ((c)->state |= MMC_STATE_SDCARD)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
...@@ -119,11 +116,4 @@ struct mmc_driver { ...@@ -119,11 +116,4 @@ struct mmc_driver {
extern int mmc_register_driver(struct mmc_driver *); extern int mmc_register_driver(struct mmc_driver *);
extern void mmc_unregister_driver(struct mmc_driver *); extern void mmc_unregister_driver(struct mmc_driver *);
static inline int mmc_card_claim_host(struct mmc_card *card)
{
return __mmc_claim_host(card->host, card);
}
#define mmc_card_release_host(c) mmc_release_host((c)->host)
#endif #endif
/*
* linux/include/linux/mmc/core.h
*
* 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.
*/
#ifndef LINUX_MMC_CORE_H
#define LINUX_MMC_CORE_H
#include <linux/interrupt.h>
#include <linux/device.h>
struct request;
struct mmc_data;
struct mmc_request;
struct mmc_command {
u32 opcode;
u32 arg;
u32 resp[4];
unsigned int flags; /* expected response type */
#define MMC_RSP_PRESENT (1 << 0)
#define MMC_RSP_136 (1 << 1) /* 136 bit response */
#define MMC_RSP_CRC (1 << 2) /* expect valid crc */
#define MMC_RSP_BUSY (1 << 3) /* card may send busy */
#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
#define MMC_CMD_MASK (3 << 5) /* command type */
#define MMC_CMD_AC (0 << 5)
#define MMC_CMD_ADTC (1 << 5)
#define MMC_CMD_BC (2 << 5)
#define MMC_CMD_BCR (3 << 5)
/*
* These are the response types, and correspond to valid bit
* patterns of the above flags. One additional valid pattern
* is all zeros, which means we don't expect a response.
*/
#define MMC_RSP_NONE (0)
#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT)
#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
/*
* These are the command types.
*/
#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK)
unsigned int retries; /* max number of retries */
unsigned int error; /* command error */
#define MMC_ERR_NONE 0
#define MMC_ERR_TIMEOUT 1
#define MMC_ERR_BADCRC 2
#define MMC_ERR_FIFO 3
#define MMC_ERR_FAILED 4
#define MMC_ERR_INVALID 5
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
};
struct mmc_data {
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */
unsigned int timeout_clks; /* data timeout (in clocks) */
unsigned int blksz; /* data block size */
unsigned int blocks; /* number of blocks */
unsigned int error; /* data error */
unsigned int flags;
#define MMC_DATA_WRITE (1 << 8)
#define MMC_DATA_READ (1 << 9)
#define MMC_DATA_STREAM (1 << 10)
#define MMC_DATA_MULTI (1 << 11)
unsigned int bytes_xfered;
struct mmc_command *stop; /* stop command */
struct mmc_request *mrq; /* associated request */
unsigned int sg_len; /* size of scatter list */
struct scatterlist *sg; /* I/O scatter list */
};
struct mmc_request {
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_command *stop;
void *done_data; /* completion data */
void (*done)(struct mmc_request *);/* completion function */
};
struct mmc_host;
struct mmc_card;
extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int);
extern void mmc_claim_host(struct mmc_host *host);
extern void mmc_release_host(struct mmc_host *host);
#endif
...@@ -10,36 +10,13 @@ ...@@ -10,36 +10,13 @@
#ifndef LINUX_MMC_HOST_H #ifndef LINUX_MMC_HOST_H
#define LINUX_MMC_HOST_H #define LINUX_MMC_HOST_H
#include <linux/mmc/mmc.h> #include <linux/mmc/core.h>
struct mmc_ios { struct mmc_ios {
unsigned int clock; /* clock rate */ unsigned int clock; /* clock rate */
unsigned short vdd; unsigned short vdd;
#define MMC_VDD_150 0 /* vdd stores the bit number of the selected voltage range from below. */
#define MMC_VDD_155 1
#define MMC_VDD_160 2
#define MMC_VDD_165 3
#define MMC_VDD_170 4
#define MMC_VDD_180 5
#define MMC_VDD_190 6
#define MMC_VDD_200 7
#define MMC_VDD_210 8
#define MMC_VDD_220 9
#define MMC_VDD_230 10
#define MMC_VDD_240 11
#define MMC_VDD_250 12
#define MMC_VDD_260 13
#define MMC_VDD_270 14
#define MMC_VDD_280 15
#define MMC_VDD_290 16
#define MMC_VDD_300 17
#define MMC_VDD_310 18
#define MMC_VDD_320 19
#define MMC_VDD_330 20
#define MMC_VDD_340 21
#define MMC_VDD_350 22
#define MMC_VDD_360 23
unsigned char bus_mode; /* command output mode */ unsigned char bus_mode; /* command output mode */
...@@ -88,6 +65,24 @@ struct mmc_host { ...@@ -88,6 +65,24 @@ struct mmc_host {
unsigned int f_max; unsigned int f_max;
u32 ocr_avail; u32 ocr_avail;
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
unsigned long caps; /* Host capabilities */ unsigned long caps; /* Host capabilities */
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
...@@ -106,6 +101,8 @@ struct mmc_host { ...@@ -106,6 +101,8 @@ struct mmc_host {
unsigned int max_blk_count; /* maximum number of blocks in one req */ unsigned int max_blk_count; /* maximum number of blocks in one req */
/* private data */ /* private data */
spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios; /* current io bus settings */ struct mmc_ios ios; /* current io bus settings */
u32 ocr; /* the current OCR setting */ u32 ocr; /* the current OCR setting */
...@@ -113,15 +110,19 @@ struct mmc_host { ...@@ -113,15 +110,19 @@ struct mmc_host {
#define MMC_MODE_MMC 0 #define MMC_MODE_MMC 0
#define MMC_MODE_SD 1 #define MMC_MODE_SD 1
struct list_head cards; /* devices attached to this host */ struct mmc_card *card; /* device attached to this host */
wait_queue_head_t wq; wait_queue_head_t wq;
spinlock_t lock; /* claimed lock */
unsigned int claimed:1; /* host exclusively claimed */ unsigned int claimed:1; /* host exclusively claimed */
struct mmc_card *card_selected; /* the selected MMC card */
struct delayed_work detect; struct delayed_work detect;
#ifdef CONFIG_MMC_DEBUG
unsigned int removed:1; /* host is being removed */
#endif
const struct mmc_bus_ops *bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */
unsigned int bus_dead:1; /* bus has been released */
unsigned long private[0] ____cacheline_aligned; unsigned long private[0] ____cacheline_aligned;
}; };
......
/* /*
* linux/include/linux/mmc/mmc.h * Header for MultiMediaCard (MMC)
* *
* This program is free software; you can redistribute it and/or modify * Copyright 2002 Hewlett-Packard Company
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. * Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
* preserved in its entirety in all copies and derived works.
*
* HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
* FITNESS FOR ANY PARTICULAR PURPOSE.
*
* Many thanks to Alessandro Rubini and Jonathan Corbet!
*
* Based strongly on code by:
*
* Author: Yong-iL Joh <tolkien@mizi.com>
* Date : $Date: 2002/06/18 12:37:30 $
*
* Author: Andrew Christian
* 15 May 2002
*/ */
#ifndef MMC_H
#define MMC_H #ifndef MMC_MMC_H
#define MMC_MMC_H
#include <linux/list.h>
#include <linux/interrupt.h> /* Standard MMC commands (4.1) type argument response */
#include <linux/device.h> /* class 1 */
#define MMC_GO_IDLE_STATE 0 /* bc */
struct request; #define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */
struct mmc_data; #define MMC_ALL_SEND_CID 2 /* bcr R2 */
struct mmc_request; #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
#define MMC_SET_DSR 4 /* bc [31:16] RCA */
struct mmc_command { #define MMC_SWITCH 6 /* ac [31:0] See below R1b */
u32 opcode; #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
u32 arg; #define MMC_SEND_EXT_CSD 8 /* adtc R1 */
u32 resp[4]; #define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */
unsigned int flags; /* expected response type */ #define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */
#define MMC_RSP_PRESENT (1 << 0) #define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
#define MMC_RSP_136 (1 << 1) /* 136 bit response */ #define MMC_STOP_TRANSMISSION 12 /* ac R1b */
#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ #define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ #define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
#define MMC_CMD_MASK (3 << 5) /* command type */ /* class 2 */
#define MMC_CMD_AC (0 << 5) #define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
#define MMC_CMD_ADTC (1 << 5) #define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
#define MMC_CMD_BC (2 << 5) #define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
#define MMC_CMD_BCR (3 << 5)
/* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
/* class 4 */
#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */
#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */
#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */
#define MMC_PROGRAM_CID 26 /* adtc R1 */
#define MMC_PROGRAM_CSD 27 /* adtc R1 */
/* class 6 */
#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */
#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */
#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */
/* class 5 */
#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */
#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */
#define MMC_ERASE 38 /* ac R1b */
/* class 9 */
#define MMC_FAST_IO 39 /* ac <Complex> R4 */
#define MMC_GO_IRQ_STATE 40 /* bcr R5 */
/* class 7 */
#define MMC_LOCK_UNLOCK 42 /* adtc R1b */
/* class 8 */
#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
/* /*
* These are the response types, and correspond to valid bit * MMC_SWITCH argument format:
* patterns of the above flags. One additional valid pattern *
* is all zeros, which means we don't expect a response. * [31:26] Always 0
* [25:24] Access Mode
* [23:16] Location of target Byte in EXT_CSD
* [15:08] Value Byte
* [07:03] Always 0
* [02:00] Command Set
*/ */
#define MMC_RSP_NONE (0)
#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT)
#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
/* /*
* These are the command types. MMC status in R1
Type
e : error bit
s : status bit
r : detected and set for the actual command response
x : detected and set during command execution. the host must poll
the card by sending status command in order to read these bits.
Clear condition
a : according to the card state
b : always related to the previous command. Reception of
a valid command will clear it (with a delay of one command)
c : clear by read
*/ */
#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK)
unsigned int retries; /* max number of retries */ #define R1_OUT_OF_RANGE (1 << 31) /* er, c */
unsigned int error; /* command error */ #define R1_ADDRESS_ERROR (1 << 30) /* erx, c */
#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */
#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */
#define R1_ERASE_PARAM (1 << 27) /* ex, c */
#define R1_WP_VIOLATION (1 << 26) /* erx, c */
#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */
#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */
#define R1_COM_CRC_ERROR (1 << 23) /* er, b */
#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */
#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */
#define R1_CC_ERROR (1 << 20) /* erx, c */
#define R1_ERROR (1 << 19) /* erx, c */
#define R1_UNDERRUN (1 << 18) /* ex, c */
#define R1_OVERRUN (1 << 17) /* ex, c */
#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */
#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */
#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
#define R1_ERASE_RESET (1 << 13) /* sr, c */
#define R1_STATUS(x) (x & 0xFFFFE000)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_APP_CMD (1 << 5) /* sr, c */
#define MMC_ERR_NONE 0 /* These are unpacked versions of the actual responses */
#define MMC_ERR_TIMEOUT 1
#define MMC_ERR_BADCRC 2
#define MMC_ERR_FIFO 3
#define MMC_ERR_FAILED 4
#define MMC_ERR_INVALID 5
struct mmc_data *data; /* data segment associated with cmd */ struct _mmc_csd {
struct mmc_request *mrq; /* associated request */ u8 csd_structure;
u8 spec_vers;
u8 taac;
u8 nsac;
u8 tran_speed;
u16 ccc;
u8 read_bl_len;
u8 read_bl_partial;
u8 write_blk_misalign;
u8 read_blk_misalign;
u8 dsr_imp;
u16 c_size;
u8 vdd_r_curr_min;
u8 vdd_r_curr_max;
u8 vdd_w_curr_min;
u8 vdd_w_curr_max;
u8 c_size_mult;
union {
struct { /* MMC system specification version 3.1 */
u8 erase_grp_size;
u8 erase_grp_mult;
} v31;
struct { /* MMC system specification version 2.2 */
u8 sector_size;
u8 erase_grp_size;
} v22;
} erase;
u8 wp_grp_size;
u8 wp_grp_enable;
u8 default_ecc;
u8 r2w_factor;
u8 write_bl_len;
u8 write_bl_partial;
u8 file_format_grp;
u8 copy;
u8 perm_write_protect;
u8 tmp_write_protect;
u8 file_format;
u8 ecc;
}; };
struct mmc_data { /*
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ * OCR bits are mostly in host.h
unsigned int timeout_clks; /* data timeout (in clocks) */ */
unsigned int blksz; /* data block size */ #define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
unsigned int blocks; /* number of blocks */
unsigned int error; /* data error */
unsigned int flags;
#define MMC_DATA_WRITE (1 << 8) /*
#define MMC_DATA_READ (1 << 9) * Card Command Classes (CCC)
#define MMC_DATA_STREAM (1 << 10) */
#define MMC_DATA_MULTI (1 << 11) #define CCC_BASIC (1<<0) /* (0) Basic protocol functions */
/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */
/* (CMD11) */
#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */
/* (CMD16,17,18) */
#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */
/* (CMD20) */
#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */
/* (CMD16,24,25,26,27) */
#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */
/* (CMD32,33,34,35,36,37,38,39) */
#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */
/* (CMD28,29,30) */
#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */
/* (CMD16,CMD42) */
#define CCC_APP_SPEC (1<<8) /* (8) Application specific */
/* (CMD55,56,57,ACMD*) */
#define CCC_IO_MODE (1<<9) /* (9) I/O mode */
/* (CMD5,39,40,52,53) */
#define CCC_SWITCH (1<<10) /* (10) High speed switch */
/* (CMD6,34,35,36,37,50) */
/* (11) Reserved */
/* (CMD?) */
unsigned int bytes_xfered; /*
* CSD field definitions
*/
struct mmc_command *stop; /* stop command */ #define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */
struct mmc_request *mrq; /* associated request */ #define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */
#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */
#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */
unsigned int sg_len; /* size of scatter list */ #define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */
struct scatterlist *sg; /* I/O scatter list */ #define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */
}; #define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */
#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */
#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */
struct mmc_request { /*
struct mmc_command *cmd; * EXT_CSD fields
struct mmc_data *data; */
struct mmc_command *stop;
void *done_data; /* completion data */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */
void (*done)(struct mmc_request *);/* completion function */ #define EXT_CSD_HS_TIMING 185 /* R/W */
}; #define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
/*
* EXT_CSD field definitions
*/
struct mmc_host; #define EXT_CSD_CMD_SET_NORMAL (1<<0)
struct mmc_card; #define EXT_CSD_CMD_SET_SECURE (1<<1)
#define EXT_CSD_CMD_SET_CPSECURE (1<<2)
extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *); #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int,
struct mmc_command *, int);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card); /*
* MMC_SWITCH access modes
*/
static inline void mmc_claim_host(struct mmc_host *host) #define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
{ #define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */
__mmc_claim_host(host, (struct mmc_card *)-1); #define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
} #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
extern void mmc_release_host(struct mmc_host *host); #endif /* MMC_MMC_PROTOCOL_H */
#endif
/*
* Header for MultiMediaCard (MMC)
*
* Copyright 2002 Hewlett-Packard Company
*
* Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
* preserved in its entirety in all copies and derived works.
*
* HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
* FITNESS FOR ANY PARTICULAR PURPOSE.
*
* Many thanks to Alessandro Rubini and Jonathan Corbet!
*
* Based strongly on code by:
*
* Author: Yong-iL Joh <tolkien@mizi.com>
* Date : $Date: 2002/06/18 12:37:30 $
*
* Author: Andrew Christian
* 15 May 2002
*/
#ifndef MMC_MMC_PROTOCOL_H
#define MMC_MMC_PROTOCOL_H
/* Standard MMC commands (4.1) type argument response */
/* class 1 */
#define MMC_GO_IDLE_STATE 0 /* bc */
#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */
#define MMC_ALL_SEND_CID 2 /* bcr R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
#define MMC_SET_DSR 4 /* bc [31:16] RCA */
#define MMC_SWITCH 6 /* ac [31:0] See below R1b */
#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
#define MMC_SEND_EXT_CSD 8 /* adtc R1 */
#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */
#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */
#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
/* class 2 */
#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
/* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
/* class 4 */
#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */
#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */
#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */
#define MMC_PROGRAM_CID 26 /* adtc R1 */
#define MMC_PROGRAM_CSD 27 /* adtc R1 */
/* class 6 */
#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */
#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */
#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */
/* class 5 */
#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */
#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */
#define MMC_ERASE 38 /* ac R1b */
/* class 9 */
#define MMC_FAST_IO 39 /* ac <Complex> R4 */
#define MMC_GO_IRQ_STATE 40 /* bcr R5 */
/* class 7 */
#define MMC_LOCK_UNLOCK 42 /* adtc R1b */
/* class 8 */
#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
/* SD commands type argument response */
/* class 0 */
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
/* class 10 */
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
/* Application commands */
#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */
/*
* MMC_SWITCH argument format:
*
* [31:26] Always 0
* [25:24] Access Mode
* [23:16] Location of target Byte in EXT_CSD
* [15:08] Value Byte
* [07:03] Always 0
* [02:00] Command Set
*/
/*
* SD_SWITCH argument format:
*
* [31] Check (0) or switch (1)
* [30:24] Reserved (0)
* [23:20] Function group 6
* [19:16] Function group 5
* [15:12] Function group 4
* [11:8] Function group 3
* [7:4] Function group 2
* [3:0] Function group 1
*/
/*
* SD_SEND_IF_COND argument format:
*
* [31:12] Reserved (0)
* [11:8] Host Voltage Supply Flags
* [7:0] Check Pattern (0xAA)
*/
/*
MMC status in R1
Type
e : error bit
s : status bit
r : detected and set for the actual command response
x : detected and set during command execution. the host must poll
the card by sending status command in order to read these bits.
Clear condition
a : according to the card state
b : always related to the previous command. Reception of
a valid command will clear it (with a delay of one command)
c : clear by read
*/
#define R1_OUT_OF_RANGE (1 << 31) /* er, c */
#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */
#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */
#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */
#define R1_ERASE_PARAM (1 << 27) /* ex, c */
#define R1_WP_VIOLATION (1 << 26) /* erx, c */
#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */
#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */
#define R1_COM_CRC_ERROR (1 << 23) /* er, b */
#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */
#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */
#define R1_CC_ERROR (1 << 20) /* erx, c */
#define R1_ERROR (1 << 19) /* erx, c */
#define R1_UNDERRUN (1 << 18) /* ex, c */
#define R1_OVERRUN (1 << 17) /* ex, c */
#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */
#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */
#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
#define R1_ERASE_RESET (1 << 13) /* sr, c */
#define R1_STATUS(x) (x & 0xFFFFE000)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_APP_CMD (1 << 5) /* sr, c */
/* These are unpacked versions of the actual responses */
struct _mmc_csd {
u8 csd_structure;
u8 spec_vers;
u8 taac;
u8 nsac;
u8 tran_speed;
u16 ccc;
u8 read_bl_len;
u8 read_bl_partial;
u8 write_blk_misalign;
u8 read_blk_misalign;
u8 dsr_imp;
u16 c_size;
u8 vdd_r_curr_min;
u8 vdd_r_curr_max;
u8 vdd_w_curr_min;
u8 vdd_w_curr_max;
u8 c_size_mult;
union {
struct { /* MMC system specification version 3.1 */
u8 erase_grp_size;
u8 erase_grp_mult;
} v31;
struct { /* MMC system specification version 2.2 */
u8 sector_size;
u8 erase_grp_size;
} v22;
} erase;
u8 wp_grp_size;
u8 wp_grp_enable;
u8 default_ecc;
u8 r2w_factor;
u8 write_bl_len;
u8 write_bl_partial;
u8 file_format_grp;
u8 copy;
u8 perm_write_protect;
u8 tmp_write_protect;
u8 file_format;
u8 ecc;
};
#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */
#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */
#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */
#define MMC_VDD_160_165 0x00000008 /* VDD voltage 1.60 - 1.65 */
#define MMC_VDD_165_170 0x00000010 /* VDD voltage 1.65 - 1.70 */
#define MMC_VDD_17_18 0x00000020 /* VDD voltage 1.7 - 1.8 */
#define MMC_VDD_18_19 0x00000040 /* VDD voltage 1.8 - 1.9 */
#define MMC_VDD_19_20 0x00000080 /* VDD voltage 1.9 - 2.0 */
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
/*
* Card Command Classes (CCC)
*/
#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */
/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */
/* (CMD11) */
#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */
/* (CMD16,17,18) */
#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */
/* (CMD20) */
#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */
/* (CMD16,24,25,26,27) */
#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */
/* (CMD32,33,34,35,36,37,38,39) */
#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */
/* (CMD28,29,30) */
#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */
/* (CMD16,CMD42) */
#define CCC_APP_SPEC (1<<8) /* (8) Application specific */
/* (CMD55,56,57,ACMD*) */
#define CCC_IO_MODE (1<<9) /* (9) I/O mode */
/* (CMD5,39,40,52,53) */
#define CCC_SWITCH (1<<10) /* (10) High speed switch */
/* (CMD6,34,35,36,37,50) */
/* (11) Reserved */
/* (CMD?) */
/*
* CSD field definitions
*/
#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */
#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */
#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */
#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */
#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */
#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */
#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */
#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */
#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */
/*
* EXT_CSD fields
*/
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_CARD_TYPE 196 /* RO */
/*
* EXT_CSD field definitions
*/
#define EXT_CSD_CMD_SET_NORMAL (1<<0)
#define EXT_CSD_CMD_SET_SECURE (1<<1)
#define EXT_CSD_CMD_SET_CPSECURE (1<<2)
#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
/*
* MMC_SWITCH access modes
*/
#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
/*
* SCR field definitions
*/
#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
/*
* SD bus widths
*/
#define SD_BUS_WIDTH_1 0
#define SD_BUS_WIDTH_4 2
#endif /* MMC_MMC_PROTOCOL_H */
/*
* include/linux/mmc/sd.h
*
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef MMC_SD_H
#define MMC_SD_H
/* SD commands type argument response */
/* class 0 */
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
/* class 10 */
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
/* Application commands */
#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */
/*
* SD_SWITCH argument format:
*
* [31] Check (0) or switch (1)
* [30:24] Reserved (0)
* [23:20] Function group 6
* [19:16] Function group 5
* [15:12] Function group 4
* [11:8] Function group 3
* [7:4] Function group 2
* [3:0] Function group 1
*/
/*
* SD_SEND_IF_COND argument format:
*
* [31:12] Reserved (0)
* [11:8] Host Voltage Supply Flags
* [7:0] Check Pattern (0xAA)
*/
/*
* SCR field definitions
*/
#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
/*
* SD bus widths
*/
#define SD_BUS_WIDTH_1 0
#define SD_BUS_WIDTH_4 2
/*
* SD_SWITCH mode
*/
#define SD_SWITCH_CHECK 0
#define SD_SWITCH_SET 1
/*
* SD_SWITCH function groups
*/
#define SD_SWITCH_GRP_ACCESS 0
/*
* SD_SWITCH access modes
*/
#define SD_SWITCH_ACCESS_DEF 0
#define SD_SWITCH_ACCESS_HS 1
#endif
...@@ -14,16 +14,16 @@ ...@@ -14,16 +14,16 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/interrupt.h> #include <linux/interrupt.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 {
FM_SET_INTERRUPT_ENABLE = 0x008, FM_SET_INTERRUPT_ENABLE = 0x008,
FM_CLEAR_INTERRUPT_ENABLE = 0x00c, FM_CLEAR_INTERRUPT_ENABLE = 0x00c,
FM_INTERRUPT_STATUS = 0x014 }; FM_INTERRUPT_STATUS = 0x014
};
/* Socket registers (relative to socket base address): */ /* Socket registers (relative to socket base address): */
enum { enum {
...@@ -58,14 +58,8 @@ enum { ...@@ -58,14 +58,8 @@ enum {
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
...@@ -73,63 +67,76 @@ enum { ...@@ -73,63 +67,76 @@ enum {
#define TIFM_SOCK_STATE_OCCUPIED 0x00000008 #define TIFM_SOCK_STATE_OCCUPIED 0x00000008
#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
#define TIFM_FIFO_READY 0x00000001
#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
#define TIFM_DMA_RESET 0x00000002
#define TIFM_DMA_TX 0x00008000
#define TIFM_DMA_EN 0x00000001
#define TIFM_DMA_TSIZE 0x0000007f
#define TIFM_DMA_RESET 0x00000002 /* Meaning of this constant is unverified */ #define TIFM_TYPE_XD 1
#define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ #define TIFM_TYPE_MS 2
#define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ #define TIFM_TYPE_SD 3
typedef enum {FM_NULL = 0, FM_XD = 0x01, FM_MS = 0x02, FM_SD = 0x03} tifm_media_id; 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 (*card_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;
}; };
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 completion *finish_me;
struct tifm_dev **sockets;
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,
void (*eject)(struct tifm_adapter *fm, struct tifm_dev *sock); struct tifm_dev *sock);
struct tifm_dev *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);
...@@ -137,11 +144,11 @@ int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, ...@@ -137,11 +144,11 @@ 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)
{ {
return dev_get_drvdata(&dev->dev); return dev_get_drvdata(&dev->dev);
} }
static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data)
...@@ -149,8 +156,4 @@ static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) ...@@ -149,8 +156,4 @@ static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data)
dev_set_drvdata(&dev->dev, data); dev_set_drvdata(&dev->dev, data);
} }
struct tifm_device_id {
tifm_media_id media_id;
};
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment