提交 62ea6d80 编写于 作者: L 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
  ...
......@@ -11,10 +11,20 @@
#include <linux/tifm.h>
#include <linux/dma-mapping.h>
#include <linux/freezer.h>
#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)
{
......@@ -22,7 +32,7 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
spin_lock_irqsave(&fm->lock, flags);
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);
}
......@@ -30,8 +40,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
{
struct tifm_adapter *fm = dev_id;
struct tifm_dev *sock;
unsigned int irq_status;
unsigned int sock_irq_status, cnt;
unsigned int irq_status, cnt;
spin_lock(&fm->lock);
irq_status = readl(fm->addr + FM_INTERRUPT_STATUS);
......@@ -45,12 +54,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
for (cnt = 0; cnt < fm->num_sockets; cnt++) {
sock = fm->sockets[cnt];
sock_irq_status = (irq_status >> cnt)
& (TIFM_IRQ_FIFOMASK(1)
| TIFM_IRQ_CARDMASK(1));
if (sock && sock_irq_status)
sock->signal_irq(sock, sock_irq_status);
if (sock) {
if ((irq_status >> cnt) & TIFM_IRQ_FIFOMASK(1))
sock->data_event(sock);
if ((irq_status >> cnt) & TIFM_IRQ_CARDMASK(1))
sock->card_event(sock);
}
}
fm->socket_change_set |= irq_status
......@@ -58,57 +67,57 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
}
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);
else
wake_up_all(&fm->change_set_notify);
tifm_queue_work(&fm->media_switcher);
spin_unlock(&fm->lock);
return IRQ_HANDLED;
}
static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr,
int is_x2)
static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr)
{
unsigned int s_state;
int cnt;
writel(0x0e00, sock_addr + SOCK_CONTROL);
for (cnt = 0; cnt < 100; cnt++) {
for (cnt = 16; cnt <= 256; cnt <<= 1) {
if (!(TIFM_SOCK_STATE_POWERED
& readl(sock_addr + SOCK_PRESENT_STATE)))
break;
msleep(10);
msleep(cnt);
}
s_state = readl(sock_addr + SOCK_PRESENT_STATE);
if (!(TIFM_SOCK_STATE_OCCUPIED & s_state))
return FM_NULL;
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);
}
return 0;
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
& readl(sock_addr + SOCK_PRESENT_STATE)))
break;
msleep(10);
msleep(cnt);
}
if (!is_x2)
writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED),
sock_addr + SOCK_CONTROL);
writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED),
sock_addr + SOCK_CONTROL);
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)
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;
unsigned long flags;
tifm_media_id media_id;
char *card_name = "xx";
int cnt, rc;
struct tifm_adapter *fm = container_of(work, struct tifm_adapter,
media_switcher);
struct tifm_dev *sock;
unsigned int socket_change_set;
while (1) {
rc = wait_event_interruptible(fm->change_set_notify,
fm->socket_change_set);
if (rc == -ERESTARTSYS)
try_to_freeze();
unsigned long flags;
unsigned char media_id;
unsigned int socket_change_set, cnt;
spin_lock_irqsave(&fm->lock, flags);
socket_change_set = fm->socket_change_set;
fm->socket_change_set = 0;
spin_lock_irqsave(&fm->lock, flags);
socket_change_set = fm->socket_change_set;
fm->socket_change_set = 0;
dev_dbg(fm->dev, "checking media set %x\n",
socket_change_set);
dev_dbg(fm->cdev.dev, "checking media set %x\n",
socket_change_set);
if (kthread_should_stop())
socket_change_set = (1 << fm->num_sockets) - 1;
if (!socket_change_set) {
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;
spin_lock_irqsave(&fm->lock, flags);
for (cnt = 0; cnt < fm->num_sockets; cnt++) {
if (!(socket_change_set & (1 << cnt)))
continue;
sock = fm->sockets[cnt];
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;
sock = fm->sockets[cnt];
if (sock) {
printk(KERN_INFO
"%s : demand removing card from socket %u:%u\n",
fm->cdev.class_id, fm->id, cnt);
fm->sockets[cnt] = NULL;
spin_unlock_irqrestore(&fm->lock, flags);
media_id = tifm_7xx1_toggle_sock_power(
tifm_7xx1_sock_addr(fm->addr, cnt),
fm->num_sockets == 2);
if (media_id) {
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);
}
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()) {
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);
} else {
for (cnt = 0; cnt < fm->num_sockets; cnt++) {
if (fm->sockets[cnt])
fm->socket_change_set |= 1 << cnt;
}
if (!fm->socket_change_set) {
spin_unlock_irqrestore(&fm->lock, flags);
return 0;
} else {
spin_unlock_irqrestore(&fm->lock, flags);
media_id = tifm_7xx1_toggle_sock_power(
tifm_7xx1_sock_addr(fm->addr, cnt));
// tifm_alloc_device will check if media_id is valid
sock = tifm_alloc_device(fm, cnt, media_id);
if (sock) {
sock->addr = tifm_7xx1_sock_addr(fm->addr, 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);
}
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
......@@ -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)
{
struct tifm_adapter *fm = pci_get_drvdata(dev);
int cnt, rc;
int rc;
unsigned int good_sockets = 0, bad_sockets = 0;
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_restore_state(dev);
......@@ -271,45 +232,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
dev_dbg(&dev->dev, "resuming host\n");
for (cnt = 0; cnt < fm->num_sockets; cnt++)
new_ids[cnt] = tifm_7xx1_toggle_sock_power(
tifm_7xx1_sock_addr(fm->addr, cnt),
fm->num_sockets == 2);
for (rc = 0; rc < fm->num_sockets; rc++)
new_ids[rc] = tifm_7xx1_toggle_sock_power(
tifm_7xx1_sock_addr(fm->addr, rc));
spin_lock_irqsave(&fm->lock, flags);
fm->socket_change_set = 0;
for (cnt = 0; cnt < fm->num_sockets; cnt++) {
if (fm->sockets[cnt]) {
if (fm->sockets[cnt]->media_id == new_ids[cnt])
fm->socket_change_set |= 1 << cnt;
fm->sockets[cnt]->media_id = new_ids[cnt];
for (rc = 0; rc < fm->num_sockets; rc++) {
if (fm->sockets[rc]) {
if (fm->sockets[rc]->type == new_ids[rc])
good_sockets |= 1 << rc;
else
bad_sockets |= 1 << rc;
}
}
writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
fm->addr + FM_SET_INTERRUPT_ENABLE);
if (!fm->socket_change_set) {
spin_unlock_irqrestore(&fm->lock, flags);
return 0;
} else {
fm->socket_change_set = 0;
dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n",
good_sockets, bad_sockets);
fm->socket_change_set = 0;
if (good_sockets) {
fm->finish_me = &finish_resume;
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);
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);
spin_unlock_irqrestore(&fm->lock, flags);
writel(TIFM_IRQ_ENABLE,
fm->addr + FM_SET_INTERRUPT_ENABLE);
fm->socket_change_set = 0;
spin_unlock_irqrestore(&fm->lock, flags);
return 0;
}
......@@ -345,20 +310,14 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
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) {
rc = -ENOMEM;
goto err_out_int;
}
fm->dev = &dev->dev;
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;
INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
fm->eject = tifm_7xx1_eject;
pci_set_drvdata(dev, fm);
......@@ -367,19 +326,16 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
if (!fm->addr)
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)
goto err_out_unmap;
init_waitqueue_head(&fm->change_set_notify);
rc = tifm_add_adapter(fm, tifm_7xx1_switch_media);
rc = tifm_add_adapter(fm);
if (rc)
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),
fm->addr + FM_SET_INTERRUPT_ENABLE);
wake_up_process(fm->media_switcher);
return 0;
err_out_irq:
......@@ -401,18 +357,12 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
static void tifm_7xx1_remove(struct pci_dev *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);
mmiowb();
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);
pci_set_drvdata(dev, NULL);
......
......@@ -14,71 +14,124 @@
#include <linux/idr.h>
#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_SPINLOCK(tifm_adapter_lock);
static tifm_media_id *tifm_device_match(tifm_media_id *ids,
struct tifm_dev *dev)
static const char *tifm_media_type_name(unsigned char type, unsigned char nt)
{
while (*ids) {
if (dev->media_id == *ids)
return ids;
ids++;
}
return NULL;
const char *card_type_name[3][3] = {
{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
{ "XD", "MS", "SD"},
{ "xd", "ms", "sd"}
};
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);
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))
if (sock->type == id->type)
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,
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 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,
"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 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
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_driver *drv = fm_dev->drv;
struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
driver);
if (drv && drv->suspend)
return drv->suspend(fm_dev, state);
if (dev->driver && drv->suspend)
return drv->suspend(sock, state);
return 0;
}
static int tifm_device_resume(struct device *dev)
{
struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
struct tifm_driver *drv = fm_dev->drv;
struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
driver);
if (drv && drv->resume)
return drv->resume(fm_dev);
if (dev->driver && drv->resume)
return drv->resume(sock);
return 0;
}
......@@ -89,19 +142,33 @@ static int tifm_device_resume(struct device *dev)
#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 = {
.name = "tifm",
.match = tifm_match,
.uevent = tifm_uevent,
.suspend = tifm_device_suspend,
.resume = tifm_device_resume
.name = "tifm",
.dev_attrs = tifm_dev_attrs,
.match = tifm_bus_match,
.uevent = tifm_uevent,
.probe = tifm_device_probe,
.remove = tifm_device_remove,
.suspend = tifm_device_suspend,
.resume = tifm_device_resume
};
static void tifm_free(struct class_device *cdev)
{
struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev);
kfree(fm->sockets);
kfree(fm);
}
......@@ -110,28 +177,25 @@ static struct class tifm_adapter_class = {
.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;
fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL);
fm = kzalloc(sizeof(struct tifm_adapter)
+ sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL);
if (fm) {
fm->cdev.class = &tifm_adapter_class;
spin_lock_init(&fm->lock);
fm->cdev.dev = dev;
class_device_initialize(&fm->cdev);
spin_lock_init(&fm->lock);
fm->num_sockets = num_sockets;
}
return fm;
}
EXPORT_SYMBOL(tifm_alloc_adapter);
void tifm_free_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 tifm_add_adapter(struct tifm_adapter *fm)
{
int rc;
......@@ -141,59 +205,80 @@ int tifm_add_adapter(struct tifm_adapter *fm,
spin_lock(&tifm_adapter_lock);
rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id);
spin_unlock(&tifm_adapter_lock);
if (!rc) {
snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
fm->media_switcher = kthread_create(mediathreadfn,
fm, "tifm/%u", fm->id);
if (!IS_ERR(fm->media_switcher))
return class_device_add(&fm->cdev);
if (rc)
return rc;
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);
idr_remove(&tifm_adapter_idr, fm->id);
spin_unlock(&tifm_adapter_lock);
rc = -ENOMEM;
}
return rc;
}
EXPORT_SYMBOL(tifm_add_adapter);
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);
idr_remove(&tifm_adapter_idr, fm->id);
spin_unlock(&tifm_adapter_lock);
class_device_del(&fm->cdev);
}
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);
kfree(fm_dev);
class_device_put(&fm->cdev);
}
EXPORT_SYMBOL(tifm_free_device);
EXPORT_SYMBOL(tifm_free_adapter);
static void tifm_dummy_signal_irq(struct tifm_dev *sock,
unsigned int sock_irq_status)
void tifm_free_device(struct device *dev)
{
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);
if (dev) {
spin_lock_init(&dev->lock);
dev->dev.parent = fm->dev;
dev->dev.bus = &tifm_bus_type;
dev->dev.release = tifm_free_device;
dev->signal_irq = tifm_dummy_signal_irq;
struct tifm_dev *sock = NULL;
if (!tifm_media_type_name(type, 0))
return sock;
sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL);
if (sock) {
spin_lock_init(&sock->lock);
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);
......@@ -218,54 +303,15 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
}
EXPORT_SYMBOL(tifm_unmap_sg);
static int tifm_device_probe(struct device *dev)
{
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)
void tifm_queue_work(struct work_struct *work)
{
struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
struct tifm_driver *drv = fm_dev->drv;
if (drv) {
fm_dev->signal_irq = tifm_dummy_signal_irq;
if (drv->remove)
drv->remove(fm_dev);
fm_dev->drv = NULL;
}
put_device(dev);
return 0;
queue_work(workqueue, work);
}
EXPORT_SYMBOL(tifm_queue_work);
int tifm_register_driver(struct tifm_driver *drv)
{
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);
}
......@@ -279,13 +325,25 @@ EXPORT_SYMBOL(tifm_unregister_driver);
static int __init tifm_init(void)
{
int rc = bus_register(&tifm_bus_type);
int rc;
if (!rc) {
rc = class_register(&tifm_adapter_class);
if (rc)
bus_unregister(&tifm_bus_type);
}
workqueue = create_freezeable_workqueue("tifm");
if (!workqueue)
return -ENOMEM;
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;
}
......@@ -294,6 +352,7 @@ static void __exit tifm_exit(void)
{
class_unregister(&tifm_adapter_class);
bus_unregister(&tifm_bus_type);
destroy_workqueue(workqueue);
}
subsys_initcall(tifm_init);
......
......@@ -19,110 +19,10 @@ config MMC_DEBUG
This is an option for use by developers; most people should
say N here. This enables MMC core and driver debugging.
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.
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.
source "drivers/mmc/core/Kconfig"
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)'.
source "drivers/mmc/card/Kconfig"
To compile this driver as a module, choose M here: the
module will be called tifm_sd.
source "drivers/mmc/host/Kconfig"
endmenu
......@@ -2,32 +2,11 @@
# 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)
EXTRA_CFLAGS += -DDEBUG
EXTRA_CFLAGS += -DDEBUG
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 @@
* Block driver for media (i.e., flash cards)
*
* Copyright 2002 Hewlett-Packard Company
* Copyright 2005-2007 Pierre Ossman
*
* Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
......@@ -31,13 +32,13 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "mmc_queue.h"
#include "queue.h"
/*
* max 8 partitions per card
......@@ -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_card *card = md->queue.card;
struct mmc_blk_request brq;
int ret = 1;
int ret = 1, sg_pos, data_size;
if (mmc_card_claim_host(card))
goto flush_queue;
mmc_claim_host(card->host);
do {
struct mmc_command cmd;
......@@ -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_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);
if (brq.cmd.error) {
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)
spin_unlock_irq(&md->lock);
} while (ret);
mmc_card_release_host(card);
mmc_release_host(card->host);
return 1;
......@@ -378,9 +392,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
spin_unlock_irq(&md->lock);
}
flush_queue:
mmc_card_release_host(card);
mmc_release_host(card->host);
spin_lock_irq(&md->lock);
while (ret) {
......@@ -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);
/*
* 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));
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
/*
* The EXT_CSD sector count is in number or 512 byte
* sectors.
*/
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;
err_putdisk:
......@@ -502,12 +523,12 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
if (mmc_card_blockaddr(card))
return 0;
mmc_card_claim_host(card);
mmc_claim_host(card->host);
cmd.opcode = MMC_SET_BLOCKLEN;
cmd.arg = 1 << md->block_bits;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 5);
mmc_card_release_host(card);
mmc_release_host(card->host);
if (err) {
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 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 version 2 as
......@@ -14,7 +15,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "mmc_queue.h"
#include "queue.h"
#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
blk_cleanup_queue(mq->queue);
return ret;
}
EXPORT_SYMBOL(mmc_init_queue);
void mmc_cleanup_queue(struct mmc_queue *mq)
{
......@@ -191,6 +191,9 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
q->queuedata = NULL;
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 */
kthread_stop(mq->thread);
......@@ -226,7 +229,6 @@ void mmc_queue_suspend(struct mmc_queue *mq)
down(&mq->thread_sem);
}
}
EXPORT_SYMBOL(mmc_queue_suspend);
/**
* mmc_queue_resume - resume a previously suspended MMC request queue
......@@ -247,4 +249,4 @@ void mmc_queue_resume(struct mmc_queue *mq)
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.
*
......@@ -18,7 +18,7 @@
#include <linux/mmc/card.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 to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
......@@ -72,12 +72,11 @@ static void mmc_release_card(struct device *dev)
/*
* This currently matches any MMC driver to any MMC card - drivers
* 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)
{
struct mmc_card *card = dev_to_mmc_card(dev);
return !mmc_card_bad(card);
return 1;
}
static int
......@@ -217,6 +216,8 @@ int mmc_register_card(struct mmc_card *card)
device_del(&card->dev);
}
}
if (ret == 0)
mmc_card_set_present(card);
return ret;
}
......
/*
* linux/drivers/mmc/mmc.h
* linux/drivers/mmc/core/sysfs.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_H
#define _MMC_H
/* core-internal functions */
#ifndef _MMC_CORE_SYSFS_H
#define _MMC_CORE_SYSFS_H
void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
int mmc_register_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);
int mmc_schedule_work(struct work_struct *work);
int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay);
void mmc_flush_scheduled_work(void);
#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 @@
#include <linux/atmel_pdc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h>
#include <asm/irq.h>
......
......@@ -42,7 +42,6 @@
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
......
......@@ -41,7 +41,6 @@
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>
#include <linux/delay.h>
#include <asm/dma.h>
......
......@@ -17,7 +17,6 @@
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/amba/bus.h>
#include <linux/clk.h>
......
......@@ -22,7 +22,6 @@
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/card.h>
#include <linux/clk.h>
......@@ -605,7 +604,7 @@ static void mmc_omap_switch_handler(struct work_struct *work)
}
if (mmc_omap_cover_is_open(host)) {
if (!complained) {
dev_info(mmc_dev(host->mmc), "cover is open");
dev_info(mmc_dev(host->mmc), "cover is open\n");
complained = 1;
}
if (mmc_omap_enable_poll)
......@@ -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);
int func_clk_rate = clk_get_rate(host->fclk);
int dsor;
int realclock, i;
realclock = ios->clock;
if (ios->clock == 0)
dsor = 0;
else {
int func_clk_rate = clk_get_rate(host->fclk);
dsor = func_clk_rate / realclock;
if (dsor < 1)
dsor = 1;
return 0;
if (func_clk_rate / dsor > realclock)
dsor++;
dsor = func_clk_rate / ios->clock;
if (dsor < 1)
dsor = 1;
if (dsor > 250)
dsor = 250;
if (func_clk_rate / dsor > ios->clock)
dsor++;
if (ios->bus_width == MMC_BUS_WIDTH_4)
dsor |= 1 << 15;
}
if (dsor > 250)
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) {
case MMC_POWER_OFF:
mmc_omap_power(host, 0);
break;
case MMC_POWER_UP:
case MMC_POWER_ON:
/* Cannot touch dsor yet, just power up MMC */
mmc_omap_power(host, 1);
return;
case MMC_POWER_ON:
dsor |= 1 << 11;
break;
}
host->bus_mode = ios->bus_mode;
host->hw_bus_mode = host->bus_mode;
clk_enable(host->fclk);
/* 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)
* Writing to the CON register twice seems to do the trick. */
for (i = 0; i < 2; i++)
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 */
OMAP_MMC_WRITE(host, IE, 0);
OMAP_MMC_WRITE(host, STAT, 0xffff);
......
......@@ -24,7 +24,6 @@
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/dma.h>
#include <asm/io.h>
......
/*
* 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
* it under the terms of the GNU General Public License as published by
......@@ -15,7 +15,6 @@
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/scatterlist.h>
......@@ -247,14 +246,13 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
chunk_remain = min(blksize, 4);
}
size = min(host->size, host->remain);
size = min(size, chunk_remain);
size = min(host->remain, chunk_remain);
chunk_remain -= size;
blksize -= size;
host->offset += size;
host->remain -= size;
host->size -= size;
while (size) {
*buffer = data & 0xFF;
buffer++;
......@@ -289,14 +287,13 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
buffer = sdhci_sg_to_buffer(host) + host->offset;
while (blksize) {
size = min(host->size, host->remain);
size = min(size, chunk_remain);
size = min(host->remain, chunk_remain);
chunk_remain -= size;
blksize -= size;
host->offset += size;
host->remain -= size;
host->size -= size;
while (size) {
data >>= 8;
data |= (u32)*buffer << 24;
......@@ -325,7 +322,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
BUG_ON(!host->data);
if (host->size == 0)
if (host->num_sg == 0)
return;
if (host->data->flags & MMC_DATA_READ)
......@@ -339,10 +336,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
else
sdhci_write_block_pio(host);
if (host->size == 0)
if (host->num_sg == 0)
break;
BUG_ON(host->num_sg == 0);
}
DBG("PIO transfer complete.\n");
......@@ -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);
} else {
host->size = data->blksz * data->blocks;
host->cur_sg = data->sg;
host->num_sg = data->sg_len;
......@@ -473,10 +466,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
"though there were blocks left.\n",
mmc_hostname(host->mmc));
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);
......@@ -669,20 +658,16 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
pwr = SDHCI_POWER_ON;
switch (power) {
case MMC_VDD_170:
case MMC_VDD_180:
case MMC_VDD_190:
switch (1 << power) {
case MMC_VDD_165_195:
pwr |= SDHCI_POWER_180;
break;
case MMC_VDD_290:
case MMC_VDD_300:
case MMC_VDD_310:
case MMC_VDD_29_30:
case MMC_VDD_30_31:
pwr |= SDHCI_POWER_300;
break;
case MMC_VDD_320:
case MMC_VDD_330:
case MMC_VDD_340:
case MMC_VDD_32_33:
case MMC_VDD_33_34:
pwr |= SDHCI_POWER_330;
break;
default:
......@@ -1294,7 +1279,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
if (caps & SDHCI_CAN_VDD_300)
mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
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) {
printk(KERN_ERR "%s: Hardware doesn't report any "
......
/*
* 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
* it under the terms of the GNU General Public License as published by
......@@ -187,8 +187,6 @@ struct sdhci_host {
int offset; /* Offset into current sg */
int remain; /* Bytes left in current */
int size; /* Remaining bytes in transfer */
char slot_descr[20]; /* Name for reservations */
int irq; /* Device IRQ */
......
/*
* 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
* it under the terms of the GNU General Public License as published by
......@@ -33,7 +33,6 @@
#include <linux/pnp.h>
#include <linux/highmem.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h>
#include <asm/dma.h>
......@@ -178,9 +177,8 @@ static void wbsd_init_device(struct wbsd_host *host)
ier = 0;
ier |= WBSD_EINT_CARD;
ier |= WBSD_EINT_FIFO_THRE;
ier |= WBSD_EINT_CCRC;
ier |= WBSD_EINT_TIMEOUT;
ier |= WBSD_EINT_CRC;
ier |= WBSD_EINT_TIMEOUT;
ier |= WBSD_EINT_TC;
outb(ier, host->base + WBSD_EIR);
......@@ -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)
{
unsigned int len, i, size;
unsigned int len, i;
struct scatterlist *sg;
char *dmabuf = host->dma_buffer;
char *sgbuf;
size = host->size;
sg = data->sg;
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++) {
sgbuf = page_address(sg[i].page) + sg[i].offset;
if (size < sg[i].length)
memcpy(dmabuf, sgbuf, size);
else
memcpy(dmabuf, sgbuf, sg[i].length);
memcpy(dmabuf, sgbuf, 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)
{
unsigned int len, i, size;
unsigned int len, i;
struct scatterlist *sg;
char *dmabuf = host->dma_buffer;
char *sgbuf;
size = host->size;
sg = data->sg;
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++) {
sgbuf = page_address(sg[i].page) + sg[i].offset;
if (size < sg[i].length)
memcpy(sgbuf, dmabuf, size);
else
memcpy(sgbuf, dmabuf, sg[i].length);
memcpy(sgbuf, 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)
/*
* Handle excessive data.
*/
if (data->bytes_xfered == host->size)
if (host->num_sg == 0)
return;
buffer = wbsd_sg_to_buffer(host) + host->offset;
......@@ -513,12 +457,6 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
data->bytes_xfered++;
/*
* Transfer done?
*/
if (data->bytes_xfered == host->size)
return;
/*
* End of scatter list entry?
*/
......@@ -526,19 +464,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
/*
* Get next entry. Check if last.
*/
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;
if (!wbsd_next_sg(host))
return;
}
buffer = wbsd_sg_to_buffer(host);
}
......@@ -550,7 +477,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
* hardware problem. The chip doesn't trigger
* 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);
}
......@@ -564,7 +491,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
* Check that we aren't being called after the
* entire buffer has been transfered.
*/
if (data->bytes_xfered == host->size)
if (host->num_sg == 0)
return;
buffer = wbsd_sg_to_buffer(host) + host->offset;
......@@ -593,12 +520,6 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
data->bytes_xfered++;
/*
* Transfer done?
*/
if (data->bytes_xfered == host->size)
return;
/*
* End of scatter list entry?
*/
......@@ -606,19 +527,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
/*
* Get next entry. Check if last.
*/
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;
if (!wbsd_next_sg(host))
return;
}
buffer = wbsd_sg_to_buffer(host);
}
......@@ -638,6 +548,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
u16 blksize;
u8 setup;
unsigned long dmaflags;
unsigned int size;
DBGF("blksz %04x blks %04x flags %08x\n",
data->blksz, data->blocks, data->flags);
......@@ -647,7 +558,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
/*
* Calculate size.
*/
host->size = data->blocks * data->blksz;
size = data->blocks * data->blksz;
/*
* Check timeout values for overflow.
......@@ -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.
*/
BUG_ON(host->size > 0x10000);
if (host->size > 0x10000) {
BUG_ON(size > 0x10000);
if (size > 0x10000) {
data->error = MMC_ERR_INVALID;
return;
}
......@@ -729,7 +640,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
else
set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
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);
release_dma_lock(dmaflags);
......@@ -812,6 +723,10 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
count = get_dma_residue(host->dma);
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?
*/
......@@ -820,7 +735,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
"%d bytes left.\n",
mmc_hostname(host->mmc), count);
data->error = MMC_ERR_FAILED;
if (data->error == MMC_ERR_NONE)
data->error = MMC_ERR_FAILED;
} else {
/*
* Transfer data from DMA buffer to
......@@ -828,8 +744,11 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
*/
if (data->flags & MMC_DATA_READ)
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)
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)) {
/*
* The hardware is so delightfully stupid that it has a list
* 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)
"supported by this controller.\n",
mmc_hostname(host->mmc), cmd->opcode);
#endif
cmd->data->error = MMC_ERR_INVALID;
if (cmd->data->stop)
wbsd_send_command(host, cmd->data->stop);
cmd->error = MMC_ERR_INVALID;
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.
*/
......@@ -1167,7 +1085,7 @@ static void wbsd_tasklet_fifo(unsigned long param)
/*
* Done?
*/
if (host->size == data->bytes_xfered) {
if (host->num_sg == 0) {
wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
tasklet_schedule(&host->finish_tasklet);
}
......@@ -1245,30 +1163,6 @@ static void wbsd_tasklet_finish(unsigned long param)
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
*/
......@@ -1299,8 +1193,6 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
tasklet_hi_schedule(&host->crc_tasklet);
if (isr & WBSD_INT_TIMEOUT)
tasklet_hi_schedule(&host->timeout_tasklet);
if (isr & WBSD_INT_BUSYEND)
tasklet_hi_schedule(&host->block_tasklet);
if (isr & WBSD_INT_TC)
tasklet_schedule(&host->finish_tasklet);
......@@ -1601,8 +1493,6 @@ static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
(unsigned long)host);
tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
(unsigned long)host);
tasklet_init(&host->block_tasklet, wbsd_tasklet_block,
(unsigned long)host);
return 0;
}
......@@ -1621,7 +1511,6 @@ static void __devexit wbsd_release_irq(struct wbsd_host *host)
tasklet_kill(&host->crc_tasklet);
tasklet_kill(&host->timeout_tasklet);
tasklet_kill(&host->finish_tasklet);
tasklet_kill(&host->block_tasklet);
}
/*
......
/*
* 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
* it under the terms of the GNU General Public License as published by
......@@ -46,10 +46,10 @@
#define WBSD_EINT_CARD 0x40
#define WBSD_EINT_FIFO_THRE 0x20
#define WBSD_EINT_CCRC 0x10
#define WBSD_EINT_CRC 0x10
#define WBSD_EINT_TIMEOUT 0x08
#define WBSD_EINT_PROGEND 0x04
#define WBSD_EINT_CRC 0x02
#define WBSD_EINT_BUSYEND 0x02
#define WBSD_EINT_TC 0x01
#define WBSD_INT_PENDING 0x80
......@@ -158,8 +158,6 @@ struct wbsd_host
unsigned int offset; /* Offset into current entry */
unsigned int remain; /* Data left in curren entry */
int size; /* Total size of transfer */
char* dma_buffer; /* ISA DMA buffer */
dma_addr_t dma_addr; /* Physical address for same */
......@@ -182,7 +180,6 @@ struct wbsd_host
struct tasklet_struct crc_tasklet;
struct tasklet_struct timeout_tasklet;
struct tasklet_struct finish_tasklet;
struct tasklet_struct block_tasklet;
struct timer_list ignore_timer; /* Ignore detection timer */
};
此差异已折叠。
#ifndef ASMARM_ARCH_MMC_H
#define ASMARM_ARCH_MMC_H
#include <linux/mmc/protocol.h>
#include <linux/mmc/host.h>
struct imxmmc_platform_data {
int (*card_present)(void);
......
#ifndef ASMARM_ARCH_MMC_H
#define ASMARM_ARCH_MMC_H
#include <linux/mmc/protocol.h>
#include <linux/mmc/host.h>
#include <linux/interrupt.h>
struct device;
......
......@@ -4,7 +4,7 @@
#ifndef ASMARM_MACH_MMC_H
#define ASMARM_MACH_MMC_H
#include <linux/mmc/protocol.h>
#include <linux/mmc/host.h>
struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
......
......@@ -10,7 +10,7 @@
#ifndef LINUX_MMC_CARD_H
#define LINUX_MMC_CARD_H
#include <linux/mmc/mmc.h>
#include <linux/mmc/core.h>
struct mmc_cid {
unsigned int manfid;
......@@ -41,6 +41,7 @@ struct mmc_csd {
struct mmc_ext_csd {
unsigned int hs_max_dtr;
unsigned int sectors;
};
struct sd_scr {
......@@ -60,18 +61,17 @@ struct mmc_host;
* MMC device
*/
struct mmc_card {
struct list_head node; /* node in hosts devices list */
struct mmc_host *host; /* the host this device belongs to */
struct device dev; /* the 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 */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */
#define MMC_STATE_BAD (1<<2) /* unrecognised device */
#define MMC_STATE_SDCARD (1<<3) /* is an SD card */
#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 */
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */
u32 raw_scr[2]; /* raw card SCR */
......@@ -82,18 +82,15 @@ struct mmc_card {
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_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_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
#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_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_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
......@@ -119,11 +116,4 @@ struct mmc_driver {
extern int mmc_register_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
/*
* 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
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* 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
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册