add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
#
# MMC core configuration
#
config MMC_UNSAFE_RESUME
bool "Assume MMC/SD cards are non-removable (DANGEROUS)"
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.
This option sets a default which can be overridden by the
module parameter "removable=0" or "removable=1".

View File

@@ -0,0 +1,15 @@
#
# 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 bus.o host.o \
mmc.o mmc_ops.o sd.o sd_ops.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o

View File

@@ -0,0 +1,295 @@
/*
* linux/drivers/mmc/core/bus.c
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 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.
*
* MMC card bus driver model
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "core.h"
#include "sdio_cis.h"
#include "bus.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)
static ssize_t mmc_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mmc_card *card = dev_to_mmc_card(dev);
switch (card->type) {
case MMC_TYPE_MMC:
return sprintf(buf, "MMC\n");
case MMC_TYPE_SD:
return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n");
default:
return -EFAULT;
}
}
static struct device_attribute mmc_dev_attrs[] = {
__ATTR(type, S_IRUGO, mmc_type_show, NULL),
__ATTR_NULL,
};
/*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method.
*/
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
static int
mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct mmc_card *card = dev_to_mmc_card(dev);
const char *type;
int retval = 0;
switch (card->type) {
case MMC_TYPE_MMC:
type = "MMC";
break;
case MMC_TYPE_SD:
type = "SD";
break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
default:
type = NULL;
}
if (type) {
retval = add_uevent_var(env, "MMC_TYPE=%s", type);
if (retval)
return retval;
}
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
if (retval)
return retval;
/*
* Request the mmc_block device. Note: that this is a direct request
* for the module it carries no information as to what is inserted.
*/
retval = add_uevent_var(env, "MODALIAS=mmc:block");
return retval;
}
static int mmc_bus_probe(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = dev_to_mmc_card(dev);
return drv->probe(card);
}
static int mmc_bus_remove(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = dev_to_mmc_card(dev);
drv->remove(card);
return 0;
}
static int mmc_bus_suspend(struct device *dev, pm_message_t state)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = dev_to_mmc_card(dev);
int ret = 0;
if (dev->driver && drv->suspend)
ret = drv->suspend(card, state);
return ret;
}
static int mmc_bus_resume(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = dev_to_mmc_card(dev);
int ret = 0;
if (dev->driver && drv->resume)
ret = drv->resume(card);
return ret;
}
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_attrs = mmc_dev_attrs,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.suspend = mmc_bus_suspend,
.resume = mmc_bus_resume,
};
int mmc_register_bus(void)
{
return bus_register(&mmc_bus_type);
}
void mmc_unregister_bus(void)
{
bus_unregister(&mmc_bus_type);
}
/**
* mmc_register_driver - register a media driver
* @drv: MMC media driver
*/
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;
return driver_register(&drv->drv);
}
EXPORT_SYMBOL(mmc_register_driver);
/**
* mmc_unregister_driver - unregister a media driver
* @drv: MMC media driver
*/
void mmc_unregister_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;
driver_unregister(&drv->drv);
}
EXPORT_SYMBOL(mmc_unregister_driver);
static void mmc_release_card(struct device *dev)
{
struct mmc_card *card = dev_to_mmc_card(dev);
sdio_free_common_cis(card);
if (card->info)
kfree(card->info);
kfree(card);
}
/*
* Allocate and initialise a new MMC card structure.
*/
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
struct mmc_card *card;
card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
card->host = host;
device_initialize(&card->dev);
card->dev.parent = mmc_classdev(host);
card->dev.bus = &mmc_bus_type;
card->dev.release = mmc_release_card;
card->dev.type = type;
return card;
}
/*
* Register a new MMC card with the driver model.
*/
int mmc_add_card(struct mmc_card *card)
{
int ret;
const char *type;
dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
switch (card->type) {
case MMC_TYPE_MMC:
type = "MMC";
break;
case MMC_TYPE_SD:
type = "SD";
if (mmc_card_blockaddr(card))
type = "SDHC";
break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
default:
type = "?";
break;
}
if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: new %s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
type);
} else {
printk(KERN_INFO "%s: new %s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
type, card->rca);
}
ret = device_add(&card->dev);
if (ret)
return ret;
#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
#endif
mmc_card_set_present(card);
return 0;
}
/*
* Unregister a new MMC card with the driver model, and
* (eventually) free it.
*/
void mmc_remove_card(struct mmc_card *card)
{
#ifdef CONFIG_DEBUG_FS
mmc_remove_card_debugfs(card);
#endif
if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: SPI card removed\n",
mmc_hostname(card->host));
} else {
printk(KERN_INFO "%s: card %04x removed\n",
mmc_hostname(card->host), card->rca);
}
device_del(&card->dev);
}
put_device(&card->dev);
}

View File

@@ -0,0 +1,31 @@
/*
* linux/drivers/mmc/core/bus.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_BUS_H
#define _MMC_CORE_BUS_H
#define MMC_DEV_ATTR(name, fmt, args...) \
static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct mmc_card *card = container_of(dev, struct mmc_card, dev); \
return sprintf(buf, fmt, args); \
} \
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
struct device_type *type);
int mmc_add_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card);
int mmc_register_bus(void);
void mmc_unregister_bus(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/*
* 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 {
int (*awake)(struct mmc_host *);
int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *);
int (*suspend)(struct mmc_host *);
int (*resume)(struct mmc_host *);
void (*power_save)(struct mmc_host *);
void (*power_restore)(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_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);
static inline void mmc_delay(unsigned int ms)
{
if (ms < 1000 / HZ) {
cond_resched();
mdelay(ms);
} else {
msleep(ms);
}
}
void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host);
int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
int mmc_attach_sd(struct mmc_host *host, u32 ocr);
int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
/* Module parameters */
extern int use_spi_crc;
/* Debugfs information for hosts and cards */
void mmc_add_host_debugfs(struct mmc_host *host);
void mmc_remove_host_debugfs(struct mmc_host *host);
void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card);
#endif

View File

@@ -0,0 +1,293 @@
/*
* Debugfs support for hosts and cards
*
* Copyright (C) 2008 Atmel Corporation
*
* 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/debugfs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "core.h"
#include "mmc_ops.h"
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
static int mmc_ios_show(struct seq_file *s, void *data)
{
static const char *vdd_str[] = {
[8] = "2.0",
[9] = "2.1",
[10] = "2.2",
[11] = "2.3",
[12] = "2.4",
[13] = "2.5",
[14] = "2.6",
[15] = "2.7",
[16] = "2.8",
[17] = "2.9",
[18] = "3.0",
[19] = "3.1",
[20] = "3.2",
[21] = "3.3",
[22] = "3.4",
[23] = "3.5",
[24] = "3.6",
};
struct mmc_host *host = s->private;
struct mmc_ios *ios = &host->ios;
const char *str;
seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
seq_printf(s, "vdd:\t\t%u ", ios->vdd);
if ((1 << ios->vdd) & MMC_VDD_165_195)
seq_printf(s, "(1.65 - 1.95 V)\n");
else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
&& vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
vdd_str[ios->vdd + 1]);
else
seq_printf(s, "(invalid)\n");
switch (ios->bus_mode) {
case MMC_BUSMODE_OPENDRAIN:
str = "open drain";
break;
case MMC_BUSMODE_PUSHPULL:
str = "push-pull";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
switch (ios->chip_select) {
case MMC_CS_DONTCARE:
str = "don't care";
break;
case MMC_CS_HIGH:
str = "active high";
break;
case MMC_CS_LOW:
str = "active low";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
switch (ios->power_mode) {
case MMC_POWER_OFF:
str = "off";
break;
case MMC_POWER_UP:
str = "up";
break;
case MMC_POWER_ON:
str = "on";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
seq_printf(s, "bus width:\t%u (%u bits)\n",
ios->bus_width, 1 << ios->bus_width);
switch (ios->timing) {
case MMC_TIMING_LEGACY:
str = "legacy";
break;
case MMC_TIMING_MMC_HS:
str = "mmc high-speed";
break;
case MMC_TIMING_SD_HS:
str = "sd high-speed";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
return 0;
}
static int mmc_ios_open(struct inode *inode, struct file *file)
{
return single_open(file, mmc_ios_show, inode->i_private);
}
static const struct file_operations mmc_ios_fops = {
.open = mmc_ios_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
root = debugfs_create_dir(mmc_hostname(host), NULL);
if (IS_ERR(root))
/* Don't complain -- debugfs just isn't enabled */
return;
if (!root)
/* Complain -- debugfs is enabled, but it failed to
* create the directory. */
goto err_root;
host->debugfs_root = root;
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
goto err_ios;
return;
err_ios:
debugfs_remove_recursive(root);
host->debugfs_root = NULL;
err_root:
dev_err(&host->class_dev, "failed to initialize debugfs\n");
}
void mmc_remove_host_debugfs(struct mmc_host *host)
{
debugfs_remove_recursive(host->debugfs_root);
}
static int mmc_dbg_card_status_get(void *data, u64 *val)
{
struct mmc_card *card = data;
u32 status;
int ret;
mmc_claim_host(card->host);
ret = mmc_send_status(data, &status);
if (!ret)
*val = status;
mmc_release_host(card->host);
return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
NULL, "%08llx\n");
#define EXT_CSD_STR_LEN 1025
static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
{
struct mmc_card *card = inode->i_private;
char *buf;
ssize_t n = 0;
u8 *ext_csd;
int err, i;
buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) {
err = -ENOMEM;
goto out_free;
}
mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host);
if (err)
goto out_free;
for (i = 511; i >= 0; i--)
n += sprintf(buf + n, "%02x", ext_csd[i]);
n += sprintf(buf + n, "\n");
BUG_ON(n != EXT_CSD_STR_LEN);
filp->private_data = buf;
kfree(ext_csd);
return 0;
out_free:
kfree(buf);
kfree(ext_csd);
return err;
}
static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos)
{
char *buf = filp->private_data;
return simple_read_from_buffer(ubuf, cnt, ppos,
buf, EXT_CSD_STR_LEN);
}
static int mmc_ext_csd_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}
static const struct file_operations mmc_dbg_ext_csd_fops = {
.open = mmc_ext_csd_open,
.read = mmc_ext_csd_read,
.release = mmc_ext_csd_release,
};
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
struct dentry *root;
if (!host->debugfs_root)
return;
root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
if (IS_ERR(root))
/* Don't complain -- debugfs just isn't enabled */
return;
if (!root)
/* Complain -- debugfs is enabled, but it failed to
* create the directory. */
goto err;
card->debugfs_root = root;
if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
goto err;
if (mmc_card_mmc(card) || mmc_card_sd(card))
if (!debugfs_create_file("status", S_IRUSR, root, card,
&mmc_dbg_card_status_fops))
goto err;
if (mmc_card_mmc(card))
if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
&mmc_dbg_ext_csd_fops))
goto err;
return;
err:
debugfs_remove_recursive(root);
card->debugfs_root = NULL;
dev_err(&card->dev, "failed to initialize debugfs\n");
}
void mmc_remove_card_debugfs(struct mmc_card *card)
{
debugfs_remove_recursive(card->debugfs_root);
}

View File

@@ -0,0 +1,187 @@
/*
* linux/drivers/mmc/core/host.c
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 2007-2008 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.
*
* MMC host class device management
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/idr.h>
#include <linux/pagemap.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/mmc/host.h>
#include "core.h"
#include "host.h"
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
static void mmc_host_classdev_release(struct device *dev)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
kfree(host);
}
static struct class mmc_host_class = {
.name = "mmc_host",
.dev_release = mmc_host_classdev_release,
};
int mmc_register_host_class(void)
{
return class_register(&mmc_host_class);
}
void mmc_unregister_host_class(void)
{
class_unregister(&mmc_host_class);
}
static DEFINE_IDR(mmc_host_idr);
static DEFINE_SPINLOCK(mmc_host_lock);
/**
* 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)
{
int err;
struct mmc_host *host;
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return NULL;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
if (!host)
return NULL;
spin_lock(&mmc_host_lock);
err = idr_get_new(&mmc_host_idr, host, &host->index);
spin_unlock(&mmc_host_lock);
if (err)
goto free;
dev_set_name(&host->class_dev, "mmc%d", host->index);
host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
/*
* 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;
free:
kfree(host);
return NULL;
}
EXPORT_SYMBOL(mmc_alloc_host);
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
*
* Register the host with the driver model. The host must be
* prepared to start servicing requests before this function
* completes.
*/
int mmc_add_host(struct mmc_host *host)
{
int err;
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
!host->ops->enable_sdio_irq);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
err = device_add(&host->class_dev);
if (err)
return err;
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
mmc_start_host(host);
register_pm_notifier(&host->pm_notify);
return 0;
}
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. No new requests will be issued
* after this function has returned.
*/
void mmc_remove_host(struct mmc_host *host)
{
unregister_pm_notifier(&host->pm_notify);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
mmc_remove_host_debugfs(host);
#endif
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);
}
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)
{
spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock);
put_device(&host->class_dev);
}
EXPORT_SYMBOL(mmc_free_host);

View File

@@ -0,0 +1,20 @@
/*
* linux/drivers/mmc/core/host.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_HOST_H
#define _MMC_CORE_HOST_H
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
void mmc_host_deeper_disable(struct work_struct *work);
#endif

View File

@@ -0,0 +1,705 @@
/*
* linux/drivers/mmc/core/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/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include "core.h"
#include "bus.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(KERN_ERR "%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(KERN_ERR "%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);
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0;
/*
* 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.\n", mmc_hostname(card->host));
return -ENOMEM;
}
err = mmc_send_ext_csd(card, ext_csd);
if (err) {
/* If the host or the card can't do the switch,
* fail more gracefully. */
if ((err != -EINVAL)
&& (err != -ENOSYS)
&& (err != -EFAULT))
goto out;
/*
* 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 = 0;
}
goto out;
}
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 5) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host),
card->ext_csd.rev);
err = -EINVAL;
goto out;
}
if (card->ext_csd.rev >= 2) {
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] & EXT_CSD_CARD_TYPE_MASK) {
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));
}
if (card->ext_csd.rev >= 3) {
u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
/* Sleep / awake timeout in 100ns units */
if (sa_shift > 0 && sa_shift <= 0x17)
card->ext_csd.sa_timeout =
1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
}
out:
kfree(ext_csd);
return err;
}
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_date.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
NULL,
};
static struct attribute_group mmc_std_attr_group = {
.attrs = mmc_std_attrs,
};
static const struct attribute_group *mmc_attr_groups[] = {
&mmc_std_attr_group,
NULL,
};
static struct device_type mmc_type = {
.groups = mmc_attr_groups,
};
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "oldcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_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);
WARN_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)
goto err;
/*
* For SPI, enable CRC as appropriate.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
goto err;
}
/*
* Fetch CID from card.
*/
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
if (err)
goto err;
if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
err = -ENOENT;
goto err;
}
card = oldcard;
} else {
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host, &mmc_type);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
}
/*
* For native busses: set card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_set_relative_addr(card);
if (err)
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)
goto free_card;
err = mmc_decode_csd(card);
if (err)
goto free_card;
err = mmc_decode_cid(card);
if (err)
goto free_card;
}
/*
* Select card, as all following commands rely on that.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
goto free_card;
}
if (!oldcard) {
/*
* Fetch and process extended CSD.
*/
err = mmc_read_ext_csd(card);
if (err)
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 && err != -EBADMSG)
goto free_card;
if (err) {
printk(KERN_WARNING "%s: switch to highspeed failed\n",
mmc_hostname(card->host));
err = 0;
} else {
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 | MMC_CAP_8_BIT_DATA))) {
unsigned ext_csd_bit, bus_width;
if (host->caps & MMC_CAP_8_BIT_DATA) {
ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
bus_width = MMC_BUS_WIDTH_8;
} else {
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
bus_width = MMC_BUS_WIDTH_4;
}
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, ext_csd_bit);
if (err && err != -EBADMSG)
goto free_card;
if (err) {
printk(KERN_WARNING "%s: switch to bus width %d "
"failed\n", mmc_hostname(card->host),
1 << bus_width);
err = 0;
} else {
mmc_set_bus_width(card->host, bus_width);
}
}
if (!oldcard)
host->card = card;
return 0;
free_card:
if (!oldcard)
mmc_remove_card(card);
err:
return err;
}
/*
* 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_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
/*
* Suspend callback from host.
*/
static int mmc_suspend(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host);
return 0;
}
/*
* 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 int mmc_resume(struct mmc_host *host)
{
int err;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
return err;
}
static void mmc_power_restore(struct mmc_host *host)
{
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
}
static int mmc_sleep(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 1);
if (err < 0)
pr_debug("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}
return err;
}
static int mmc_awake(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 0);
if (err < 0)
pr_debug("%s: Error %d while awaking sleeping card",
mmc_hostname(host), err);
}
return err;
}
static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = NULL,
.resume = NULL,
.power_restore = mmc_power_restore,
};
static const struct mmc_bus_ops mmc_ops_unsafe = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
.power_restore = mmc_power_restore,
};
static void mmc_attach_bus_ops(struct mmc_host *host)
{
const struct mmc_bus_ops *bus_ops;
if (!mmc_card_is_removable(host))
bus_ops = &mmc_ops_unsafe;
else
bus_ops = &mmc_ops;
mmc_attach_bus(host, bus_ops);
}
/*
* Starting point for MMC card init.
*/
int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
{
int err;
BUG_ON(!host);
WARN_ON(!host->claimed);
mmc_attach_bus_ops(host);
/*
* We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_read_ocr(host, 1, &ocr);
if (err)
goto err;
}
/*
* 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) {
err = -EINVAL;
goto err;
}
/*
* Detect and init the card.
*/
err = mmc_init_card(host, host->ocr, NULL);
if (err)
goto err;
mmc_release_host(host);
err = mmc_add_card(host->card);
if (err)
goto remove_card;
return 0;
remove_card:
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err);
return err;
}

View File

@@ -0,0 +1,464 @@
/*
* linux/drivers/mmc/core/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/slab.h>
#include <linux/types.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)
return err;
return 0;
}
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_card_sleepawake(struct mmc_host *host, int sleep)
{
struct mmc_command cmd;
struct mmc_card *card = host->card;
int err;
if (sleep)
mmc_deselect_cards(host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
if (sleep)
cmd.arg |= 1 << 15;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
/*
* If the host does not wait while the card signals busy, then we will
* will have to wait the sleep/awake timeout. Note, we cannot use the
* SEND_STATUS command to poll the status because that command (and most
* others) is invalid while the card sleeps.
*/
if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
if (!sleep)
err = mmc_select_card(card);
return err;
}
int mmc_go_idle(struct mmc_host *host)
{
int err;
struct mmc_command cmd;
/*
* Non-SPI hosts need to prevent chipselect going active during
* GO_IDLE; that would put chips into SPI mode. Remind them of
* that in case of hardware that won't pull up DAT3/nCS otherwise.
*
* SPI hosts ignore ios.chip_select; it's managed according to
* rules that must accomodate non-MMC slaves which this layer
* won't even know about.
*/
if (!mmc_host_is_spi(host)) {
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_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
err = mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1);
if (!mmc_host_is_spi(host)) {
mmc_set_chip_select(host, MMC_CS_DONTCARE);
mmc_delay(1);
}
host->use_spi_crc = 0;
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 = mmc_host_is_spi(host) ? 0 : ocr;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10);
}
if (rocr && !mmc_host_is_spi(host))
*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)
return err;
memcpy(cid, cmd.resp, sizeof(u32) * 4);
return 0;
}
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)
return err;
return 0;
}
static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{
int err;
struct mmc_command cmd;
BUG_ON(!host);
BUG_ON(!cxd);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = opcode;
cmd.arg = arg;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
memcpy(cxd, cmd.resp, sizeof(u32) * 4);
return 0;
}
static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
void *data_buf;
/* dma onto stack is unsafe/nonportable, but callers to this
* routine normally provide temporary on-stack buffers ...
*/
data_buf = kmalloc(len, GFP_KERNEL);
if (data_buf == NULL)
return -ENOMEM;
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 = opcode;
cmd.arg = 0;
/* NOTE HACK: the MMC_RSP_SPI_R1 is always correct here, but we
* rely on callers to never use this with "native" calls for reading
* CSD or CID. Native versions of those commands use the R2 type,
* not R1 plus a data block.
*/
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = len;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, data_buf, len);
if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) {
/*
* The spec states that CSR and CID accesses have a timeout
* of 64 clock cycles.
*/
data.timeout_ns = 0;
data.timeout_clks = 64;
} else
mmc_set_data_timeout(&data, card);
mmc_wait_for_req(host, &mrq);
memcpy(buf, data_buf, len);
kfree(data_buf);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
return 0;
}
int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
int ret, i;
if (!mmc_host_is_spi(card->host))
return mmc_send_cxd_native(card->host, card->rca << 16,
csd, MMC_SEND_CSD);
ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
if (ret)
return ret;
for (i = 0;i < 4;i++)
csd[i] = be32_to_cpu(csd[i]);
return 0;
}
int mmc_send_cid(struct mmc_host *host, u32 *cid)
{
int ret, i;
if (!mmc_host_is_spi(host)) {
if (!host->card)
return -EINVAL;
return mmc_send_cxd_native(host, host->card->rca << 16,
cid, MMC_SEND_CID);
}
ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
if (ret)
return ret;
for (i = 0;i < 4;i++)
cid[i] = be32_to_cpu(cid[i]);
return 0;
}
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
ext_csd, 512);
}
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
{
struct mmc_command cmd;
int err;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SPI_READ_OCR;
cmd.arg = highcap ? (1 << 30) : 0;
cmd.flags = MMC_RSP_SPI_R3;
err = mmc_wait_for_cmd(host, &cmd, 0);
*ocrp = cmd.resp[1];
return err;
}
int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
{
struct mmc_command cmd;
int err;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SPI_CRC_ON_OFF;
cmd.flags = MMC_RSP_SPI_R1;
cmd.arg = use_crc;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (!err)
host->use_spi_crc = use_crc;
return err;
}
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{
int err;
struct mmc_command cmd;
u32 status;
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_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* Must check status to be sure of no errors */
do {
err = mmc_send_status(card, &status);
if (err)
return err;
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
break;
if (mmc_host_is_spi(card->host))
break;
} while (R1_CURRENT_STATE(status) == 7);
if (mmc_host_is_spi(card->host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
return -EBADMSG;
} else {
if (status & 0xFDFFA000)
printk(KERN_WARNING "%s: unexpected status %#x after "
"switch", mmc_hostname(card->host), status);
if (status & R1_SWITCH_ERROR)
return -EBADMSG;
}
return 0;
}
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;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
if (status)
*status = cmd.resp[0];
return 0;
}

View File

@@ -0,0 +1,31 @@
/*
* linux/drivers/mmc/core/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);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
#endif

View File

@@ -0,0 +1,716 @@
/*
* linux/drivers/mmc/core/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/slab.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 "bus.h"
#include "mmc_ops.h"
#include "sd_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 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(KERN_ERR "%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];
resp[3] = card->raw_scr[1];
resp[2] = card->raw_scr[0];
scr_struct = UNSTUFF_BITS(resp, 60, 4);
if (scr_struct != 0) {
printk(KERN_ERR "%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;
if (card->scr.sda_vsn < SCR_SPEC_VER_1)
return 0;
if (!(card->csd.cmdclass & CCC_SWITCH)) {
printk(KERN_WARNING "%s: card lacks mandatory switch "
"function, performance might suffer.\n",
mmc_hostname(card->host));
return 0;
}
err = -EIO;
status = kmalloc(64, GFP_KERNEL);
if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM;
}
err = mmc_sd_switch(card, 0, 0, 1, status);
if (err) {
/* If the host or the card can't do the switch,
* fail more gracefully. */
if ((err != -EINVAL)
&& (err != -ENOSYS)
&& (err != -EFAULT))
goto out;
printk(KERN_WARNING "%s: problem reading switch "
"capabilities, performance might suffer.\n",
mmc_hostname(card->host));
err = 0;
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->scr.sda_vsn < SCR_SPEC_VER_1)
return 0;
if (!(card->csd.cmdclass & CCC_SWITCH))
return 0;
if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
return 0;
if (card->sw_caps.hs_max_dtr == 0)
return 0;
err = -EIO;
status = kmalloc(64, GFP_KERNEL);
if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM;
}
err = mmc_sd_switch(card, 1, 0, 1, status);
if (err)
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;
}
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
static struct attribute *sd_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_scr.attr,
&dev_attr_date.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
NULL,
};
static struct attribute_group sd_std_attr_group = {
.attrs = sd_std_attrs,
};
static const struct attribute_group *sd_attr_groups[] = {
&sd_std_attr_group,
NULL,
};
static struct device_type sd_type = {
.groups = sd_attr_groups,
};
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "oldcard" 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);
WARN_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)
ocr |= 1 << 30;
err = mmc_send_app_op_cond(host, ocr, NULL);
if (err)
goto err;
/*
* Fetch CID from card.
*/
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
if (err)
goto err;
if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
err = -ENOENT;
goto err;
}
card = oldcard;
} else {
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host, &sd_type);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
}
/*
* For native busses: get card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
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)
goto free_card;
err = mmc_decode_csd(card);
if (err)
goto free_card;
mmc_decode_cid(card);
}
/*
* Select card, as all following commands rely on that.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
goto free_card;
}
if (!oldcard) {
/*
* Fetch SCR from card.
*/
err = mmc_app_send_scr(card, card->raw_scr);
if (err)
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)
goto free_card;
}
/*
* For SPI, enable CRC as appropriate.
* This CRC enable is located AFTER the reading of the
* card registers because some SDHC cards are not able
* to provide valid CRCs for non-512-byte blocks.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
goto free_card;
}
/*
* Attempt to change to high-speed (if supported)
*/
err = mmc_switch_hs(card);
if (err)
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)
goto free_card;
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
/*
* Check if read-only switch is active.
*/
if (!oldcard) {
if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {
printk(KERN_WARNING "%s: host does not "
"support reading read-only "
"switch. assuming write-enable.\n",
mmc_hostname(host));
} else {
if (host->ops->get_ro(host) > 0)
mmc_card_set_readonly(card);
}
}
if (!oldcard)
host->card = card;
return 0;
free_card:
if (!oldcard)
mmc_remove_card(card);
err:
return err;
}
/*
* 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_sd_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
/*
* Suspend callback from host.
*/
static int mmc_sd_suspend(struct mmc_host *host)
{
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host);
return 0;
}
/*
* 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 int 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);
mmc_release_host(host);
return err;
}
static void mmc_sd_power_restore(struct mmc_host *host)
{
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host);
}
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
.suspend = NULL,
.resume = NULL,
.power_restore = mmc_sd_power_restore,
};
static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore,
};
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
{
const struct mmc_bus_ops *bus_ops;
if (!mmc_card_is_removable(host))
bus_ops = &mmc_sd_ops_unsafe;
else
bus_ops = &mmc_sd_ops;
mmc_attach_bus(host, bus_ops);
}
/*
* Starting point for SD card init.
*/
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
int err;
BUG_ON(!host);
WARN_ON(!host->claimed);
mmc_sd_attach_bus_ops(host);
/*
* We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
mmc_go_idle(host);
err = mmc_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
/*
* 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) {
err = -EINVAL;
goto err;
}
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, NULL);
if (err)
goto err;
mmc_release_host(host);
err = mmc_add_card(host->card);
if (err)
goto remove_card;
return 0;
remove_card:
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err);
return err;
}

View File

@@ -0,0 +1,348 @@
/*
* linux/drivers/mmc/core/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 <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"
static 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_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
}
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
/* Check that card supported application commands */
if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
return -EOPNOTSUPP;
return 0;
}
/**
* mmc_wait_for_app_cmd - start an application command and wait for
completion
* @host: MMC host to start command
* @card: Card 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 = -EIO;
/*
* 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++) {
err = mmc_app_cmd(host, card);
if (err) {
/* no point in retrying; no APP commands allowed */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
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)
break;
/* no point in retrying illegal APP commands */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
}
return err;
}
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
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 -EINVAL;
}
err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
return 0;
}
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;
if (mmc_host_is_spi(host))
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
else
cmd.arg = ocr;
cmd.flags = MMC_RSP_SPI_R1 | 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)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10);
}
if (rocr && !mmc_host_is_spi(host))
*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;
u8 result_pattern;
/*
* 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_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
if (mmc_host_is_spi(host))
result_pattern = cmd.resp[1] & 0xFF;
else
result_pattern = cmd.resp[0] & 0xFF;
if (result_pattern != test_pattern)
return -EIO;
return 0;
}
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)
return err;
*rca = cmd.resp[0] >> 16;
return 0;
}
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);
/* NOTE: caller guarantees scr is heap-allocated */
err = mmc_app_cmd(card->host, card);
if (err)
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_SPI_R1 | 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);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
scr[0] = be32_to_cpu(scr[0]);
scr[1] = be32_to_cpu(scr[1]);
return 0;
}
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);
/* NOTE: caller guarantees resp is heap-allocated */
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_SPI_R1 | 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);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
return 0;
}

View File

@@ -0,0 +1,24 @@
/*
* linux/drivers/mmc/core/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_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

View File

@@ -0,0 +1,638 @@
/*
* linux/drivers/mmc/sdio.c
*
* 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/err.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "core.h"
#include "bus.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
#include "sdio_ops.h"
#include "sdio_cis.h"
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
unsigned char data;
ret = mmc_io_rw_direct(func->card, 0, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
if (ret)
goto out;
data &= 0x0f;
if (data == 0x0f) {
ret = mmc_io_rw_direct(func->card, 0, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data);
if (ret)
goto out;
}
func->class = data;
out:
return ret;
}
static int sdio_init_func(struct mmc_card *card, unsigned int fn)
{
int ret;
struct sdio_func *func;
BUG_ON(fn > SDIO_MAX_FUNCS);
func = sdio_alloc_func(card);
if (IS_ERR(func))
return PTR_ERR(func);
func->num = fn;
ret = sdio_read_fbr(func);
if (ret)
goto fail;
ret = sdio_read_func_cis(func);
if (ret)
goto fail;
card->sdio_func[fn - 1] = func;
return 0;
fail:
/*
* It is okay to remove the function here even though we hold
* the host lock as we haven't registered the device yet.
*/
sdio_remove_func(func);
return ret;
}
static int sdio_read_cccr(struct mmc_card *card)
{
int ret;
int cccr_vsn;
unsigned char data;
memset(&card->cccr, 0, sizeof(struct sdio_cccr));
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
if (ret)
goto out;
cccr_vsn = data & 0x0f;
if (cccr_vsn > SDIO_CCCR_REV_1_20) {
printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
mmc_hostname(card->host), cccr_vsn);
return -EINVAL;
}
card->cccr.sdio_vsn = (data & 0xf0) >> 4;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
if (ret)
goto out;
if (data & SDIO_CCCR_CAP_SMB)
card->cccr.multi_block = 1;
if (data & SDIO_CCCR_CAP_LSC)
card->cccr.low_speed = 1;
if (data & SDIO_CCCR_CAP_4BLS)
card->cccr.wide_bus = 1;
if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
if (ret)
goto out;
if (data & SDIO_POWER_SMPC)
card->cccr.high_power = 1;
}
if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
if (ret)
goto out;
if (data & SDIO_SPEED_SHS)
card->cccr.high_speed = 1;
}
out:
return ret;
}
static int sdio_enable_wide(struct mmc_card *card)
{
int ret;
u8 ctrl;
if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
return 0;
if (card->cccr.low_speed && !card->cccr.wide_bus)
return 0;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
if (ret)
return ret;
ctrl |= SDIO_BUS_WIDTH_4BIT;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
if (ret)
return ret;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
return 0;
}
/*
* If desired, disconnect the pull-up resistor on CD/DAT[3] (pin 1)
* of the card. This may be required on certain setups of boards,
* controllers and embedded sdio device which do not need the card's
* pull-up. As a result, card detection is disabled and power is saved.
*/
static int sdio_disable_cd(struct mmc_card *card)
{
int ret;
u8 ctrl;
if (!card->cccr.disable_cd)
return 0;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
if (ret)
return ret;
ctrl |= SDIO_BUS_CD_DISABLE;
return mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
}
/*
* Devices that remain active during a system suspend are
* put back into 1-bit mode.
*/
static int sdio_disable_wide(struct mmc_card *card)
{
int ret;
u8 ctrl;
if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
return 0;
if (card->cccr.low_speed && !card->cccr.wide_bus)
return 0;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
if (ret)
return ret;
if (!(ctrl & SDIO_BUS_WIDTH_4BIT))
return 0;
ctrl &= ~SDIO_BUS_WIDTH_4BIT;
ctrl |= SDIO_BUS_ASYNC_INT;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
if (ret)
return ret;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_1);
return 0;
}
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
static int sdio_enable_hs(struct mmc_card *card)
{
int ret;
u8 speed;
if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
return 0;
if (!card->cccr.high_speed)
return 0;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
if (ret)
return ret;
speed |= SDIO_SPEED_EHS;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (ret)
return ret;
mmc_card_set_highspeed(card);
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
return 0;
}
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "oldcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard, int powered_resume)
{
struct mmc_card *card;
int err;
BUG_ON(!host);
WARN_ON(!host->claimed);
/*
* Inform the card of the voltage
*/
if (!powered_resume) {
err = mmc_send_io_op_cond(host, host->ocr, &ocr);
if (err)
goto err;
}
/*
* For SPI, enable CRC as appropriate.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
goto err;
}
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host, NULL);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
card->type = MMC_TYPE_SDIO;
/*
* Call the optional HC's init_card function to handle quirks.
*/
if (host->ops->init_card)
host->ops->init_card(host, card);
/*
* For native busses: set card RCA and quit open drain mode.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto remove;
/*
* Update oldcard with the new RCA received from the SDIO
* device -- we're doing this so that it's updated in the
* "card" struct when oldcard overwrites that later.
*/
if (oldcard)
oldcard->rca = card->rca;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
/*
* Select card, as all following commands rely on that.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
goto remove;
}
/*
* Read the common registers.
*/
err = sdio_read_cccr(card);
if (err)
goto remove;
/*
* Read the common CIS tuples.
*/
err = sdio_read_common_cis(card);
if (err)
goto remove;
if (oldcard) {
int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device);
mmc_remove_card(card);
if (!same) {
err = -ENOENT;
goto err;
}
card = oldcard;
return 0;
}
/*
* Switch to high-speed (if supported).
*/
err = sdio_enable_hs(card);
if (err)
goto remove;
/*
* Change to the card's maximum speed.
*/
if (mmc_card_highspeed(card)) {
/*
* The SDIO specification doesn't mention how
* the CIS transfer speed register relates to
* high-speed, but it seems that 50 MHz is
* mandatory.
*/
mmc_set_clock(host, 50000000);
} else {
mmc_set_clock(host, card->cis.max_dtr);
}
/*
* Switch to wider bus (if supported).
*/
err = sdio_enable_wide(card);
if (err)
goto remove;
if (!oldcard)
host->card = card;
return 0;
remove:
if (!oldcard)
mmc_remove_card(card);
err:
return err;
}
/*
* Host is being removed. Free up the current card.
*/
static void mmc_sdio_remove(struct mmc_host *host)
{
int i;
BUG_ON(!host);
BUG_ON(!host->card);
for (i = 0;i < host->card->sdio_funcs;i++) {
if (host->card->sdio_func[i]) {
sdio_remove_func(host->card->sdio_func[i]);
host->card->sdio_func[i] = NULL;
}
}
mmc_remove_card(host->card);
host->card = NULL;
}
/*
* Card detection callback from host.
*/
static void mmc_sdio_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_select_card(host->card);
mmc_release_host(host);
if (err) {
mmc_sdio_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
/*
* SDIO suspend. We need to suspend all functions separately.
* Therefore all registered functions must have drivers with suspend
* and resume methods. Failing that we simply remove the whole card.
*/
static int mmc_sdio_suspend(struct mmc_host *host)
{
int i, err = 0;
for (i = 0; i < host->card->sdio_funcs; i++) {
struct sdio_func *func = host->card->sdio_func[i];
if (func && sdio_func_present(func) && func->dev.driver) {
const struct dev_pm_ops *pmops = func->dev.driver->pm;
if (!pmops || !pmops->suspend || !pmops->resume) {
/* force removal of entire card in that case */
err = -ENOSYS;
} else
err = pmops->suspend(&func->dev);
if (err)
break;
}
}
while (err && --i >= 0) {
struct sdio_func *func = host->card->sdio_func[i];
if (func && sdio_func_present(func) && func->dev.driver) {
const struct dev_pm_ops *pmops = func->dev.driver->pm;
pmops->resume(&func->dev);
}
}
if (!err && host->pm_flags & MMC_PM_KEEP_POWER) {
mmc_claim_host(host);
sdio_disable_wide(host->card);
mmc_release_host(host);
}
return err;
}
static int mmc_sdio_resume(struct mmc_host *host)
{
int i, err;
BUG_ON(!host);
BUG_ON(!host->card);
/* Basic card reinitialization. */
mmc_claim_host(host);
err = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER));
if (!err)
/* We may have switched to 1-bit mode during suspend. */
err = sdio_enable_wide(host->card);
if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host);
mmc_release_host(host);
/*
* If the card looked to be the same as before suspending, then
* we proceed to resume all card functions. If one of them returns
* an error then we simply return that error to the core and the
* card will be redetected as new. It is the responsibility of
* the function driver to perform further tests with the extra
* knowledge it has of the card to confirm the card is indeed the
* same as before suspending (same MAC address for network cards,
* etc.) and return an error otherwise.
*/
for (i = 0; !err && i < host->card->sdio_funcs; i++) {
struct sdio_func *func = host->card->sdio_func[i];
if (func && sdio_func_present(func) && func->dev.driver) {
const struct dev_pm_ops *pmops = func->dev.driver->pm;
err = pmops->resume(&func->dev);
}
}
return err;
}
static const struct mmc_bus_ops mmc_sdio_ops = {
.remove = mmc_sdio_remove,
.detect = mmc_sdio_detect,
.suspend = mmc_sdio_suspend,
.resume = mmc_sdio_resume,
};
/*
* Starting point for SDIO card init.
*/
int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
{
int err;
int i, funcs;
struct mmc_card *card;
BUG_ON(!host);
WARN_ON(!host->claimed);
mmc_attach_bus(host, &mmc_sdio_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(s) of the card(s)?
*/
if (!host->ocr) {
err = -EINVAL;
goto err;
}
/*
* Detect and init the card.
*/
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
if (err)
goto err;
card = host->card;
/*
* The number of functions on the card is encoded inside
* the ocr.
*/
funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0;
/*
* If needed, disconnect card detection pull-up resistor.
*/
err = sdio_disable_cd(card);
if (err)
goto remove;
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
}
mmc_release_host(host);
/*
* First add the card to the driver model...
*/
err = mmc_add_card(host->card);
if (err)
goto remove_added;
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
return 0;
remove_added:
/* Remove without lock if the device has been added. */
mmc_sdio_remove(host);
mmc_claim_host(host);
remove:
/* And with lock if it hasn't been added. */
if (host->card)
mmc_sdio_remove(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
mmc_hostname(host), err);
return err;
}

View File

@@ -0,0 +1,263 @@
/*
* linux/drivers/mmc/core/sdio_bus.c
*
* 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 as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* SDIO function driver model
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_cis.h"
#include "sdio_bus.h"
/* show configuration fields */
#define sdio_config_attr(field, format_string) \
static ssize_t \
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct sdio_func *func; \
\
func = dev_to_sdio_func (dev); \
return sprintf (buf, format_string, func->field); \
}
sdio_config_attr(class, "0x%02x\n");
sdio_config_attr(vendor, "0x%04x\n");
sdio_config_attr(device, "0x%04x\n");
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sdio_func *func = dev_to_sdio_func (dev);
return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
func->class, func->vendor, func->device);
}
static struct device_attribute sdio_dev_attrs[] = {
__ATTR_RO(class),
__ATTR_RO(vendor),
__ATTR_RO(device),
__ATTR_RO(modalias),
__ATTR_NULL,
};
static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
const struct sdio_device_id *id)
{
if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
return NULL;
if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
return NULL;
if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
return NULL;
return id;
}
static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
struct sdio_driver *sdrv)
{
const struct sdio_device_id *ids;
ids = sdrv->id_table;
if (ids) {
while (ids->class || ids->vendor || ids->device) {
if (sdio_match_one(func, ids))
return ids;
ids++;
}
}
return NULL;
}
static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct sdio_driver *sdrv = to_sdio_driver(drv);
if (sdio_match_device(func, sdrv))
return 1;
return 0;
}
static int
sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct sdio_func *func = dev_to_sdio_func(dev);
if (add_uevent_var(env,
"SDIO_CLASS=%02X", func->class))
return -ENOMEM;
if (add_uevent_var(env,
"SDIO_ID=%04X:%04X", func->vendor, func->device))
return -ENOMEM;
if (add_uevent_var(env,
"MODALIAS=sdio:c%02Xv%04Xd%04X",
func->class, func->vendor, func->device))
return -ENOMEM;
return 0;
}
static int sdio_bus_probe(struct device *dev)
{
struct sdio_driver *drv = to_sdio_driver(dev->driver);
struct sdio_func *func = dev_to_sdio_func(dev);
const struct sdio_device_id *id;
int ret;
id = sdio_match_device(func, drv);
if (!id)
return -ENODEV;
/* Set the default block size so the driver is sure it's something
* sensible. */
sdio_claim_host(func);
ret = sdio_set_block_size(func, 0);
sdio_release_host(func);
if (ret)
return ret;
return drv->probe(func, id);
}
static int sdio_bus_remove(struct device *dev)
{
struct sdio_driver *drv = to_sdio_driver(dev->driver);
struct sdio_func *func = dev_to_sdio_func(dev);
drv->remove(func);
if (func->irq_handler) {
printk(KERN_WARNING "WARNING: driver %s did not remove "
"its interrupt handler!\n", drv->name);
sdio_claim_host(func);
sdio_release_irq(func);
sdio_release_host(func);
}
return 0;
}
static struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_attrs = sdio_dev_attrs,
.match = sdio_bus_match,
.uevent = sdio_bus_uevent,
.probe = sdio_bus_probe,
.remove = sdio_bus_remove,
};
int sdio_register_bus(void)
{
return bus_register(&sdio_bus_type);
}
void sdio_unregister_bus(void)
{
bus_unregister(&sdio_bus_type);
}
/**
* sdio_register_driver - register a function driver
* @drv: SDIO function driver
*/
int sdio_register_driver(struct sdio_driver *drv)
{
drv->drv.name = drv->name;
drv->drv.bus = &sdio_bus_type;
return driver_register(&drv->drv);
}
EXPORT_SYMBOL_GPL(sdio_register_driver);
/**
* sdio_unregister_driver - unregister a function driver
* @drv: SDIO function driver
*/
void sdio_unregister_driver(struct sdio_driver *drv)
{
drv->drv.bus = &sdio_bus_type;
driver_unregister(&drv->drv);
}
EXPORT_SYMBOL_GPL(sdio_unregister_driver);
static void sdio_release_func(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
sdio_free_func_cis(func);
if (func->info)
kfree(func->info);
kfree(func);
}
/*
* Allocate and initialise a new SDIO function structure.
*/
struct sdio_func *sdio_alloc_func(struct mmc_card *card)
{
struct sdio_func *func;
func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
if (!func)
return ERR_PTR(-ENOMEM);
func->card = card;
device_initialize(&func->dev);
func->dev.parent = &card->dev;
func->dev.bus = &sdio_bus_type;
func->dev.release = sdio_release_func;
return func;
}
/*
* Register a new SDIO function with the driver model.
*/
int sdio_add_func(struct sdio_func *func)
{
int ret;
dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
ret = device_add(&func->dev);
if (ret == 0)
sdio_func_set_present(func);
return ret;
}
/*
* Unregister a SDIO function with the driver model, and
* (eventually) free it.
* This function can be called through error paths where sdio_add_func() was
* never executed (because a failure occurred at an earlier point).
*/
void sdio_remove_func(struct sdio_func *func)
{
if (!sdio_func_present(func))
return;
device_del(&func->dev);
put_device(&func->dev);
}

View File

@@ -0,0 +1,22 @@
/*
* linux/drivers/mmc/core/sdio_bus.h
*
* 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 as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef _MMC_CORE_SDIO_BUS_H
#define _MMC_CORE_SDIO_BUS_H
struct sdio_func *sdio_alloc_func(struct mmc_card *card);
int sdio_add_func(struct sdio_func *func);
void sdio_remove_func(struct sdio_func *func);
int sdio_register_bus(void);
void sdio_unregister_bus(void);
#endif

View File

@@ -0,0 +1,412 @@
/*
* linux/drivers/mmc/core/sdio_cis.c
*
* Author: Nicolas Pitre
* Created: June 11, 2007
* Copyright: MontaVista Software Inc.
*
* 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 as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_cis.h"
#include "sdio_ops.h"
static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned i, nr_strings;
char **buffer, *string;
/* Find all null-terminated (including zero length) strings in
the TPLLV1_INFO field. Trailing garbage is ignored. */
buf += 2;
size -= 2;
nr_strings = 0;
for (i = 0; i < size; i++) {
if (buf[i] == 0xff)
break;
if (buf[i] == 0)
nr_strings++;
}
if (nr_strings == 0)
return 0;
size = i;
buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
string = (char*)(buffer + nr_strings);
for (i = 0; i < nr_strings; i++) {
buffer[i] = string;
strcpy(string, buf);
string += strlen(string) + 1;
buf += strlen(buf) + 1;
}
if (func) {
func->num_info = nr_strings;
func->info = (const char**)buffer;
} else {
card->num_info = nr_strings;
card->info = (const char**)buffer;
}
return 0;
}
static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned int vendor, device;
/* TPLMID_MANF */
vendor = buf[0] | (buf[1] << 8);
/* TPLMID_CARD */
device = buf[2] | (buf[3] << 8);
if (func) {
func->vendor = vendor;
func->device = device;
} else {
card->cis.vendor = vendor;
card->cis.device = device;
}
return 0;
}
static const unsigned char speed_val[16] =
{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static const unsigned int speed_unit[8] =
{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
const unsigned char *, unsigned);
struct cis_tpl {
unsigned char code;
unsigned char min_size;
tpl_parse_t *parse;
};
static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
const char *tpl_descr,
const struct cis_tpl *tpl, int tpl_count,
unsigned char code,
const unsigned char *buf, unsigned size)
{
int i, ret;
/* look for a matching code in the table */
for (i = 0; i < tpl_count; i++, tpl++) {
if (tpl->code == code)
break;
}
if (i < tpl_count) {
if (size >= tpl->min_size) {
if (tpl->parse)
ret = tpl->parse(card, func, buf, size);
else
ret = -EILSEQ; /* known tuple, not parsed */
} else {
/* invalid tuple */
ret = -EINVAL;
}
if (ret && ret != -EILSEQ && ret != -ENOENT) {
printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host), tpl_descr, code, size);
}
} else {
/* unknown tuple */
ret = -ENOENT;
}
return ret;
}
static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
/* Only valid for the common CIS (function 0) */
if (func)
return -EINVAL;
/* TPLFE_FN0_BLK_SIZE */
card->cis.blksize = buf[1] | (buf[2] << 8);
/* TPLFE_MAX_TRAN_SPEED */
card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *
speed_unit[buf[3] & 7];
return 0;
}
static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned vsn;
unsigned min_size;
/* Only valid for the individual function's CIS (1-7) */
if (!func)
return -EINVAL;
/*
* This tuple has a different length depending on the SDIO spec
* version.
*/
vsn = func->card->cccr.sdio_vsn;
min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
if (size < min_size)
return -EINVAL;
/* TPLFE_MAX_BLK_SIZE */
func->max_blksize = buf[12] | (buf[13] << 8);
/* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */
if (vsn > SDIO_SDIO_REV_1_00)
func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10;
else
func->enable_timeout = jiffies_to_msecs(HZ);
return 0;
}
/*
* Known TPLFE_TYPEs table for CISTPL_FUNCE tuples.
*
* Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending
* on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO
* TPLFID_FUNCTION is always hardcoded to 0x0C.
*/
static const struct cis_tpl cis_tpl_funce_list[] = {
{ 0x00, 4, cistpl_funce_common },
{ 0x01, 0, cistpl_funce_func },
{ 0x04, 1+1+6, /* CISTPL_FUNCE_LAN_NODE_ID */ },
};
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
if (size < 1)
return -EINVAL;
return cis_tpl_parse(card, func, "CISTPL_FUNCE",
cis_tpl_funce_list,
ARRAY_SIZE(cis_tpl_funce_list),
buf[0], buf, size);
}
/* Known TPL_CODEs table for CIS tuples */
static const struct cis_tpl cis_tpl_list[] = {
{ 0x15, 3, cistpl_vers_1 },
{ 0x20, 4, cistpl_manfid },
{ 0x21, 2, /* cistpl_funcid */ },
{ 0x22, 0, cistpl_funce },
};
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
{
int ret;
struct sdio_func_tuple *this, **prev;
unsigned i, ptr = 0;
/*
* Note that this works for the common CIS (function number 0) as
* well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
* have the same offset.
*/
for (i = 0; i < 3; i++) {
unsigned char x, fn;
if (func)
fn = func->num;
else
fn = 0;
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
if (ret)
return ret;
ptr |= x << (i * 8);
}
if (func)
prev = &func->tuples;
else
prev = &card->tuples;
BUG_ON(*prev);
do {
unsigned char tpl_code, tpl_link;
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
if (ret)
break;
/* 0xff means we're done */
if (tpl_code == 0xff)
break;
/* null entries have no link field or data */
if (tpl_code == 0x00)
continue;
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
if (ret)
break;
/* a size of 0xff also means we're done */
if (tpl_link == 0xff)
break;
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
if (!this)
return -ENOMEM;
for (i = 0; i < tpl_link; i++) {
ret = mmc_io_rw_direct(card, 0, 0,
ptr + i, 0, &this->data[i]);
if (ret)
break;
}
if (ret) {
kfree(this);
break;
}
/* Try to parse the CIS tuple */
ret = cis_tpl_parse(card, func, "CIS",
cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
tpl_code, this->data, tpl_link);
if (ret == -EILSEQ || ret == -ENOENT) {
/*
* The tuple is unknown or known but not parsed.
* Queue the tuple for the function driver.
*/
this->next = NULL;
this->code = tpl_code;
this->size = tpl_link;
*prev = this;
prev = &this->next;
if (ret == -ENOENT) {
/* warn about unknown tuples */
printk(KERN_WARNING "%s: queuing unknown"
" CIS tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host),
tpl_code, tpl_link);
}
/* keep on analyzing tuples */
ret = 0;
} else {
/*
* We don't need the tuple anymore if it was
* successfully parsed by the SDIO core or if it is
* not going to be queued for a driver.
*/
kfree(this);
}
ptr += tpl_link;
} while (!ret);
/*
* Link in all unknown tuples found in the common CIS so that
* drivers don't have to go digging in two places.
*/
if (func)
*prev = card->tuples;
return ret;
}
int sdio_read_common_cis(struct mmc_card *card)
{
return sdio_read_cis(card, NULL);
}
void sdio_free_common_cis(struct mmc_card *card)
{
struct sdio_func_tuple *tuple, *victim;
tuple = card->tuples;
while (tuple) {
victim = tuple;
tuple = tuple->next;
kfree(victim);
}
card->tuples = NULL;
}
int sdio_read_func_cis(struct sdio_func *func)
{
int ret;
ret = sdio_read_cis(func->card, func);
if (ret)
return ret;
/*
* Since we've linked to tuples in the card structure,
* we must make sure we have a reference to it.
*/
get_device(&func->card->dev);
/*
* Vendor/device id is optional for function CIS, so
* copy it from the card structure as needed.
*/
if (func->vendor == 0) {
func->vendor = func->card->cis.vendor;
func->device = func->card->cis.device;
}
return 0;
}
void sdio_free_func_cis(struct sdio_func *func)
{
struct sdio_func_tuple *tuple, *victim;
tuple = func->tuples;
while (tuple && tuple != func->card->tuples) {
victim = tuple;
tuple = tuple->next;
kfree(victim);
}
func->tuples = NULL;
/*
* We have now removed the link to the tuples in the
* card structure, so remove the reference.
*/
put_device(&func->card->dev);
}

View File

@@ -0,0 +1,23 @@
/*
* linux/drivers/mmc/core/sdio_cis.h
*
* Author: Nicolas Pitre
* Created: June 11, 2007
* Copyright: MontaVista Software Inc.
*
* 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_SDIO_CIS_H
#define _MMC_SDIO_CIS_H
int sdio_read_common_cis(struct mmc_card *card);
void sdio_free_common_cis(struct mmc_card *card);
int sdio_read_func_cis(struct sdio_func *func);
void sdio_free_func_cis(struct sdio_func *func);
#endif

View File

@@ -0,0 +1,721 @@
/*
* linux/drivers/mmc/core/sdio_io.c
*
* Copyright 2007-2008 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/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
/**
* sdio_claim_host - exclusively claim a bus for a certain SDIO function
* @func: SDIO function that will be accessed
*
* Claim a bus for a set of operations. The SDIO function given
* is used to figure out which bus is relevant.
*/
void sdio_claim_host(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
mmc_claim_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_claim_host);
/**
* sdio_release_host - release a bus for a certain SDIO function
* @func: SDIO function that was accessed
*
* Release a bus, allowing others to claim the bus for their
* operations.
*/
void sdio_release_host(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
mmc_release_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_release_host);
/**
* sdio_enable_func - enables a SDIO function for usage
* @func: SDIO function to enable
*
* Powers up and activates a SDIO function so that register
* access is possible.
*/
int sdio_enable_func(struct sdio_func *func)
{
int ret;
unsigned char reg;
unsigned long timeout;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
if (ret)
goto err;
reg |= 1 << func->num;
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
if (ret)
goto err;
timeout = jiffies + msecs_to_jiffies(func->enable_timeout);
while (1) {
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);
if (ret)
goto err;
if (reg & (1 << func->num))
break;
ret = -ETIME;
if (time_after(jiffies, timeout))
goto err;
}
pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
return 0;
err:
pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
return ret;
}
EXPORT_SYMBOL_GPL(sdio_enable_func);
/**
* sdio_disable_func - disable a SDIO function
* @func: SDIO function to disable
*
* Powers down and deactivates a SDIO function. Register access
* to this function will fail until the function is reenabled.
*/
int sdio_disable_func(struct sdio_func *func)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
if (ret)
goto err;
reg &= ~(1 << func->num);
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
if (ret)
goto err;
pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
return 0;
err:
pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
return -EIO;
}
EXPORT_SYMBOL_GPL(sdio_disable_func);
/**
* sdio_set_block_size - set the block size of an SDIO function
* @func: SDIO function to change
* @blksz: new block size or 0 to use the default.
*
* The default block size is the largest supported by both the function
* and the host, with a maximum of 512 to ensure that arbitrarily sized
* data transfer use the optimal (least) number of commands.
*
* A driver may call this to override the default block size set by the
* core. This can be used to set a block size greater than the maximum
* that reported by the card; it is the driver's responsibility to ensure
* it uses a value that the card supports.
*
* Returns 0 on success, -EINVAL if the host does not support the
* requested block size, or -EIO (etc.) if one of the resultant FBR block
* size register writes failed.
*
*/
int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
{
int ret;
if (blksz > func->card->host->max_blk_size)
return -EINVAL;
if (blksz == 0) {
blksz = min(func->max_blksize, func->card->host->max_blk_size);
blksz = min(blksz, 512u);
}
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
blksz & 0xff, NULL);
if (ret)
return ret;
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
(blksz >> 8) & 0xff, NULL);
if (ret)
return ret;
func->cur_blksize = blksz;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_set_block_size);
/*
* Calculate the maximum byte mode transfer size
*/
static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
{
unsigned mval = min(func->card->host->max_seg_size,
func->card->host->max_blk_size);
if (mmc_blksz_for_byte_mode(func->card))
mval = min(mval, func->cur_blksize);
else
mval = min(mval, func->max_blksize);
return min(mval, 512u); /* maximum size for byte mode */
}
/**
* sdio_align_size - pads a transfer size to a more optimal value
* @func: SDIO function
* @sz: original transfer size
*
* Pads the original data size with a number of extra bytes in
* order to avoid controller bugs and/or performance hits
* (e.g. some controllers revert to PIO for certain sizes).
*
* If possible, it will also adjust the size so that it can be
* handled in just a single request.
*
* Returns the improved size, which might be unmodified.
*/
unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
{
unsigned int orig_sz;
unsigned int blk_sz, byte_sz;
unsigned chunk_sz;
orig_sz = sz;
/*
* Do a first check with the controller, in case it
* wants to increase the size up to a point where it
* might need more than one block.
*/
sz = mmc_align_data_size(func->card, sz);
/*
* If we can still do this with just a byte transfer, then
* we're done.
*/
if (sz <= sdio_max_byte_size(func))
return sz;
if (func->card->cccr.multi_block) {
/*
* Check if the transfer is already block aligned
*/
if ((sz % func->cur_blksize) == 0)
return sz;
/*
* Realign it so that it can be done with one request,
* and recheck if the controller still likes it.
*/
blk_sz = ((sz + func->cur_blksize - 1) /
func->cur_blksize) * func->cur_blksize;
blk_sz = mmc_align_data_size(func->card, blk_sz);
/*
* This value is only good if it is still just
* one request.
*/
if ((blk_sz % func->cur_blksize) == 0)
return blk_sz;
/*
* We failed to do one request, but at least try to
* pad the remainder properly.
*/
byte_sz = mmc_align_data_size(func->card,
sz % func->cur_blksize);
if (byte_sz <= sdio_max_byte_size(func)) {
blk_sz = sz / func->cur_blksize;
return blk_sz * func->cur_blksize + byte_sz;
}
} else {
/*
* We need multiple requests, so first check that the
* controller can handle the chunk size;
*/
chunk_sz = mmc_align_data_size(func->card,
sdio_max_byte_size(func));
if (chunk_sz == sdio_max_byte_size(func)) {
/*
* Fix up the size of the remainder (if any)
*/
byte_sz = orig_sz % chunk_sz;
if (byte_sz) {
byte_sz = mmc_align_data_size(func->card,
byte_sz);
}
return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
}
}
/*
* The controller is simply incapable of transferring the size
* we want in decent manner, so just return the original size.
*/
return orig_sz;
}
EXPORT_SYMBOL_GPL(sdio_align_size);
/* Split an arbitrarily sized data transfer into several
* IO_RW_EXTENDED commands. */
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
unsigned addr, int incr_addr, u8 *buf, unsigned size)
{
unsigned remainder = size;
unsigned max_blocks;
int ret;
/* Do the bulk of the transfer using block mode (if supported). */
if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
/* Blocks per command is limited by host count, host transfer
* size (we only use a single sg entry) and the maximum for
* IO_RW_EXTENDED of 511 blocks. */
max_blocks = min(func->card->host->max_blk_count,
func->card->host->max_seg_size / func->cur_blksize);
max_blocks = min(max_blocks, 511u);
while (remainder > func->cur_blksize) {
unsigned blocks;
blocks = remainder / func->cur_blksize;
if (blocks > max_blocks)
blocks = max_blocks;
size = blocks * func->cur_blksize;
ret = mmc_io_rw_extended(func->card, write,
func->num, addr, incr_addr, buf,
blocks, func->cur_blksize);
if (ret)
return ret;
remainder -= size;
buf += size;
if (incr_addr)
addr += size;
}
}
/* Write the remainder using byte mode. */
while (remainder > 0) {
size = min(remainder, sdio_max_byte_size(func));
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
incr_addr, buf, 1, size);
if (ret)
return ret;
remainder -= size;
buf += size;
if (incr_addr)
addr += size;
}
return 0;
}
/**
* sdio_readb - read a single byte from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a single byte from the address space of a given SDIO
* function. If there is a problem reading the address, 0xff
* is returned and @err_ret will contain the error code.
*/
u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
{
int ret;
u8 val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_readb);
/**
* sdio_writeb - write a single byte to a SDIO function
* @func: SDIO function to access
* @b: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a single byte to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)
{
int ret;
BUG_ON(!func);
ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writeb);
/**
* sdio_writeb_readb - write and read a byte from SDIO function
* @func: SDIO function to access
* @write_byte: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Performs a RAW (Read after Write) operation as defined by SDIO spec -
* single byte is written to address space of a given SDIO function and
* response is read back from the same address, both using single request.
* If there is a problem with the operation, 0xff is returned and
* @err_ret will contain the error code.
*/
u8 sdio_writeb_readb(struct sdio_func *func, u8 write_byte,
unsigned int addr, int *err_ret)
{
int ret;
u8 val;
ret = mmc_io_rw_direct(func->card, 1, func->num, addr,
write_byte, &val);
if (err_ret)
*err_ret = ret;
if (ret)
val = 0xff;
return val;
}
EXPORT_SYMBOL_GPL(sdio_writeb_readb);
/**
* sdio_memcpy_fromio - read a chunk of memory from a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
* @addr: address to begin reading from
* @count: number of bytes to read
*
* Reads from the address space of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
unsigned int addr, int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
/**
* sdio_memcpy_toio - write a chunk of memory to a SDIO function
* @func: SDIO function to access
* @addr: address to start writing to
* @src: buffer that contains the data to write
* @count: number of bytes to write
*
* Writes to the address space of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
void *src, int count)
{
return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
}
EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
/**
* sdio_readsb - read from a FIFO on a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
* @addr: address of (single byte) FIFO
* @count: number of bytes to read
*
* Reads from the specified FIFO of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_readsb);
/**
* sdio_writesb - write to a FIFO of a SDIO function
* @func: SDIO function to access
* @addr: address of (single byte) FIFO
* @src: buffer that contains the data to write
* @count: number of bytes to write
*
* Writes to the specified FIFO of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
int count)
{
return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
}
EXPORT_SYMBOL_GPL(sdio_writesb);
/**
* sdio_readw - read a 16 bit integer from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a 16 bit integer from the address space of a given SDIO
* function. If there is a problem reading the address, 0xffff
* is returned and @err_ret will contain the error code.
*/
u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret)
{
int ret;
if (err_ret)
*err_ret = 0;
ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFFFF;
}
return le16_to_cpup((__le16 *)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readw);
/**
* sdio_writew - write a 16 bit integer to a SDIO function
* @func: SDIO function to access
* @b: integer to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a 16 bit integer to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret)
{
int ret;
*(__le16 *)func->tmpbuf = cpu_to_le16(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writew);
/**
* sdio_readl - read a 32 bit integer from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a 32 bit integer from the address space of a given SDIO
* function. If there is a problem reading the address,
* 0xffffffff is returned and @err_ret will contain the error
* code.
*/
u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret)
{
int ret;
if (err_ret)
*err_ret = 0;
ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFFFFFFFF;
}
return le32_to_cpup((__le32 *)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readl);
/**
* sdio_writel - write a 32 bit integer to a SDIO function
* @func: SDIO function to access
* @b: integer to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a 32 bit integer to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret)
{
int ret;
*(__le32 *)func->tmpbuf = cpu_to_le32(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writel);
/**
* sdio_f0_readb - read a single byte from SDIO function 0
* @func: an SDIO function of the card
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a single byte from the address space of SDIO function 0.
* If there is a problem reading the address, 0xff is returned
* and @err_ret will contain the error code.
*/
unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
unsigned char val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_f0_readb);
/**
* sdio_f0_writeb - write a single byte to SDIO function 0
* @func: an SDIO function of the card
* @b: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a single byte to the address space of SDIO function 0.
* @err_ret will contain the status of the actual transfer.
*
* Only writes to the vendor specific CCCR registers (0xF0 -
* 0xFF) are permiited; @err_ret will be set to -EINVAL for *
* writes outside this range.
*/
void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
int *err_ret)
{
int ret;
BUG_ON(!func);
if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) {
if (err_ret)
*err_ret = -EINVAL;
return;
}
ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_f0_writeb);
/**
* sdio_get_host_pm_caps - get host power management capabilities
* @func: SDIO function attached to host
*
* Returns a capability bitmask corresponding to power management
* features supported by the host controller that the card function
* might rely upon during a system suspend. The host doesn't need
* to be claimed, nor the function active, for this information to be
* obtained.
*/
mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
return func->card->host->pm_caps;
}
EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps);
/**
* sdio_set_host_pm_flags - set wanted host power management capabilities
* @func: SDIO function attached to host
*
* Set a capability bitmask corresponding to wanted host controller
* power management features for the upcoming suspend state.
* This must be called, if needed, each time the suspend method of
* the function driver is called, and must contain only bits that
* were returned by sdio_get_host_pm_caps().
* The host doesn't need to be claimed, nor the function active,
* for this information to be set.
*/
int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
{
struct mmc_host *host;
BUG_ON(!func);
BUG_ON(!func->card);
host = func->card->host;
if (flags & ~host->pm_caps)
return -EINVAL;
/* function suspend methods are serialized, hence no lock needed */
host->pm_flags |= flags;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);

View File

@@ -0,0 +1,273 @@
/*
* linux/drivers/mmc/core/sdio_irq.c
*
* Author: Nicolas Pitre
* Created: June 18, 2007
* Copyright: MontaVista Software Inc.
*
* Copyright 2008 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/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
static int process_sdio_pending_irqs(struct mmc_card *card)
{
int i, ret, count;
unsigned char pending;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
if (ret) {
printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
mmc_card_id(card), ret);
return ret;
}
count = 0;
for (i = 1; i <= 7; i++) {
if (pending & (1 << i)) {
struct sdio_func *func = card->sdio_func[i - 1];
if (!func) {
printk(KERN_WARNING "%s: pending IRQ for "
"non-existant function\n",
mmc_card_id(card));
ret = -EINVAL;
} else if (func->irq_handler) {
func->irq_handler(func);
count++;
} else {
printk(KERN_WARNING "%s: pending IRQ with no handler\n",
sdio_func_id(func));
ret = -EINVAL;
}
}
}
if (count)
return count;
return ret;
}
static int sdio_irq_thread(void *_host)
{
struct mmc_host *host = _host;
struct sched_param param = { .sched_priority = 1 };
unsigned long period, idle_period;
int ret;
sched_setscheduler(current, SCHED_FIFO, &param);
/*
* We want to allow for SDIO cards to work even on non SDIO
* aware hosts. One thing that non SDIO host cannot do is
* asynchronous notification of pending SDIO card interrupts
* hence we poll for them in that case.
*/
idle_period = msecs_to_jiffies(10);
period = (host->caps & MMC_CAP_SDIO_IRQ) ?
MAX_SCHEDULE_TIMEOUT : idle_period;
pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
mmc_hostname(host), period);
do {
/*
* We claim the host here on drivers behalf for a couple
* reasons:
*
* 1) it is already needed to retrieve the CCCR_INTx;
* 2) we want the driver(s) to clear the IRQ condition ASAP;
* 3) we need to control the abort condition locally.
*
* Just like traditional hard IRQ handlers, we expect SDIO
* IRQ handlers to be quick and to the point, so that the
* holding of the host lock does not cover too much work
* that doesn't require that lock to be held.
*/
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret)
break;
ret = process_sdio_pending_irqs(host->card);
mmc_release_host(host);
/*
* Give other threads a chance to run in the presence of
* errors.
*/
if (ret < 0) {
set_current_state(TASK_INTERRUPTIBLE);
if (!kthread_should_stop())
schedule_timeout(HZ);
set_current_state(TASK_RUNNING);
}
/*
* Adaptive polling frequency based on the assumption
* that an interrupt will be closely followed by more.
* This has a substantial benefit for network devices.
*/
if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
if (ret > 0)
period /= 2;
else {
period++;
if (period > idle_period)
period = idle_period;
}
}
set_current_state(TASK_INTERRUPTIBLE);
if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 1);
if (!kthread_should_stop())
schedule_timeout(period);
set_current_state(TASK_RUNNING);
} while (!kthread_should_stop());
if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 0);
pr_debug("%s: IRQ thread exiting with code %d\n",
mmc_hostname(host), ret);
return ret;
}
static int sdio_card_irq_get(struct mmc_card *card)
{
struct mmc_host *host = card->host;
WARN_ON(!host->claimed);
if (!host->sdio_irqs++) {
atomic_set(&host->sdio_irq_thread_abort, 0);
host->sdio_irq_thread =
kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
mmc_hostname(host));
if (IS_ERR(host->sdio_irq_thread)) {
int err = PTR_ERR(host->sdio_irq_thread);
host->sdio_irqs--;
return err;
}
}
return 0;
}
static int sdio_card_irq_put(struct mmc_card *card)
{
struct mmc_host *host = card->host;
WARN_ON(!host->claimed);
BUG_ON(host->sdio_irqs < 1);
if (!--host->sdio_irqs) {
atomic_set(&host->sdio_irq_thread_abort, 1);
kthread_stop(host->sdio_irq_thread);
}
return 0;
}
/**
* sdio_claim_irq - claim the IRQ for a SDIO function
* @func: SDIO function
* @handler: IRQ handler callback
*
* Claim and activate the IRQ for the given SDIO function. The provided
* handler will be called when that IRQ is asserted. The host is always
* claimed already when the handler is called so the handler must not
* call sdio_claim_host() nor sdio_release_host().
*/
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
if (func->irq_handler) {
pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
return -EBUSY;
}
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
if (ret)
return ret;
reg |= 1 << func->num;
reg |= 1; /* Master interrupt enable */
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
if (ret)
return ret;
func->irq_handler = handler;
ret = sdio_card_irq_get(func->card);
if (ret)
func->irq_handler = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(sdio_claim_irq);
/**
* sdio_release_irq - release the IRQ for a SDIO function
* @func: SDIO function
*
* Disable and release the IRQ for the given SDIO function.
*/
int sdio_release_irq(struct sdio_func *func)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
if (func->irq_handler) {
func->irq_handler = NULL;
sdio_card_irq_put(func->card);
}
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
if (ret)
return ret;
reg &= ~(1 << func->num);
/* Disable master interrupt with the last function interrupt */
if (!(reg & 0xFE))
reg = 0;
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_release_irq);

View File

@@ -0,0 +1,208 @@
/*
* linux/drivers/mmc/sdio_ops.c
*
* 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/scatterlist.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include "core.h"
#include "sdio_ops.h"
int mmc_send_io_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_IO_SEND_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
/*
* Both R1_SPI_IDLE and MMC_CARD_BUSY indicate
* an initialized card under SPI, but some cards
* (Marvell's) only behave when looking at this
* one.
*/
if (cmd.resp[1] & MMC_CARD_BUSY)
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];
return err;
}
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
unsigned addr, u8 in, u8 *out)
{
struct mmc_command cmd;
int err;
BUG_ON(!host);
BUG_ON(fn > 7);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_RW_DIRECT;
cmd.arg = write ? 0x80000000 : 0x00000000;
cmd.arg |= fn << 28;
cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
cmd.arg |= addr << 9;
cmd.arg |= in;
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
if (mmc_host_is_spi(host)) {
/* host driver already reported errors */
} else {
if (cmd.resp[0] & R5_ERROR)
return -EIO;
if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
}
if (out) {
if (mmc_host_is_spi(host))
*out = (cmd.resp[0] >> 8) & 0xFF;
else
*out = cmd.resp[0] & 0xFF;
}
return 0;
}
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8 *out)
{
BUG_ON(!card);
return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out);
}
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(fn > 7);
BUG_ON(blocks == 1 && blksz > 512);
WARN_ON(blocks == 0);
WARN_ON(blksz == 0);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
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_IO_RW_EXTENDED;
cmd.arg = write ? 0x80000000 : 0x00000000;
cmd.arg |= fn << 28;
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
cmd.arg |= addr << 9;
if (blocks == 1 && blksz <= 512)
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
else
cmd.arg |= 0x08000000 | blocks; /* block mode */
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
data.blksz = blksz;
data.blocks = blocks;
data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, buf, blksz * blocks);
mmc_set_data_timeout(&data, card);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
if (mmc_host_is_spi(card->host)) {
/* host driver already reported errors */
} else {
if (cmd.resp[0] & R5_ERROR)
return -EIO;
if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
}
return 0;
}
int sdio_reset(struct mmc_host *host)
{
int ret;
u8 abort;
/* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */
ret = mmc_io_rw_direct_host(host, 0, 0, SDIO_CCCR_ABORT, 0, &abort);
if (ret)
abort = 0x08;
else
abort |= 0x08;
ret = mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL);
return ret;
}

View File

@@ -0,0 +1,23 @@
/*
* linux/drivers/mmc/sdio_ops.c
*
* 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_SDIO_OPS_H
#define _MMC_SDIO_OPS_H
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out);
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
int sdio_reset(struct mmc_host *host);
#endif