Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (66 commits) mmc: add new sdhci-pxa driver for Marvell SoCs mmc: make number of mmcblk minors configurable mmc_spi: Recover from CRC errors for r/w operation over SPI. mmc: sdhci-pltfm: add -pltfm driver for imx35/51 mmc: sdhci-of-esdhc: factor out common stuff mmc: sdhci_pltfm: pass more data on custom init call mmc: sdhci: introduce get_ro private write-protect hook mmc: sdhci-pltfm: move .h file into appropriate subdir mmc: sdhci-pltfm: Add structure for host-specific data mmc: fix cb710 kconfig dependency warning mmc: cb710: remove debugging printk (info duplicated from mmc-core) mmc: cb710: clear irq handler on init() error path mmc: cb710: remove unnecessary msleep() mmc: cb710: implement get_cd() callback mmc: cb710: partially demystify clock selection mmc: add a file to debugfs for changing host clock at runtime mmc: sdhci: allow for eMMC 74 clock generation by controller mmc: sdhci: highspeed: check for mmc as well as sd cards mmc: sdhci: Add Moorestown device support mmc: sdhci: Intel Medfield support ...
This commit is contained in:
commit
00ebb6382b
|
@ -2520,6 +2520,12 @@ Your cooperation is appreciated.
|
|||
8 = /dev/mmcblk1 Second SD/MMC card
|
||||
...
|
||||
|
||||
The start of next SD/MMC card can be configured with
|
||||
CONFIG_MMC_BLOCK_MINORS, or overridden at boot/modprobe
|
||||
time using the mmcblk.perdev_minors option. That would
|
||||
bump the offset between each card to be the configured
|
||||
value instead of the default 8.
|
||||
|
||||
179 char CCube DVXChip-based PCI products
|
||||
0 = /dev/dvxirq0 First DVX device
|
||||
1 = /dev/dvxirq1 Second DVX device
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/* linux/arch/arm/plat-pxa/include/plat/sdhci.h
|
||||
*
|
||||
* Copyright 2010 Marvell
|
||||
* Zhangfei Gao <zhangfei.gao@marvell.com>
|
||||
*
|
||||
* PXA Platform - SDHCI platform data definitions
|
||||
*
|
||||
* 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 __PLAT_PXA_SDHCI_H
|
||||
#define __PLAT_PXA_SDHCI_H
|
||||
|
||||
/* pxa specific flag */
|
||||
/* Require clock free running */
|
||||
#define PXA_FLAG_DISABLE_CLOCK_GATING (1<<0)
|
||||
|
||||
/*
|
||||
* struct pxa_sdhci_platdata() - Platform device data for PXA SDHCI
|
||||
* @max_speed: the maximum speed supported
|
||||
* @quirks: quirks of specific device
|
||||
* @flags: flags for platform requirement
|
||||
*/
|
||||
struct sdhci_pxa_platdata {
|
||||
unsigned int max_speed;
|
||||
unsigned int quirks;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
#endif /* __PLAT_PXA_SDHCI_H */
|
|
@ -2,9 +2,7 @@
|
|||
# Makefile for the kernel mmc device drivers.
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_MMC) += core/
|
||||
obj-$(CONFIG_MMC) += card/
|
||||
|
|
|
@ -14,6 +14,23 @@ config MMC_BLOCK
|
|||
mount the filesystem. Almost everyone wishing MMC support
|
||||
should say Y or M here.
|
||||
|
||||
config MMC_BLOCK_MINORS
|
||||
int "Number of minors per block device"
|
||||
range 4 256
|
||||
default 8
|
||||
help
|
||||
Number of minors per block device. One is needed for every
|
||||
partition on the disk (plus one for the whole disk).
|
||||
|
||||
Number of total MMC minors available is 256, so your number
|
||||
of supported block devices will be limited to 256 divided
|
||||
by this number.
|
||||
|
||||
Default is 8 to be backwards compatible with previous
|
||||
hardwired device numbering.
|
||||
|
||||
If unsure, say 8 here.
|
||||
|
||||
config MMC_BLOCK_BOUNCE
|
||||
bool "Use bounce buffer for simple hosts"
|
||||
depends on MMC_BLOCK
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
# Makefile for MMC/SD card drivers
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
|
||||
mmc_block-objs := block.o queue.o
|
||||
obj-$(CONFIG_MMC_TEST) += mmc_test.o
|
||||
|
|
|
@ -43,15 +43,27 @@
|
|||
#include "queue.h"
|
||||
|
||||
MODULE_ALIAS("mmc:block");
|
||||
|
||||
/*
|
||||
* max 8 partitions per card
|
||||
*/
|
||||
#define MMC_SHIFT 3
|
||||
#define MMC_NUM_MINORS (256 >> MMC_SHIFT)
|
||||
#ifdef MODULE_PARAM_PREFIX
|
||||
#undef MODULE_PARAM_PREFIX
|
||||
#endif
|
||||
#define MODULE_PARAM_PREFIX "mmcblk."
|
||||
|
||||
static DEFINE_MUTEX(block_mutex);
|
||||
static DECLARE_BITMAP(dev_use, MMC_NUM_MINORS);
|
||||
|
||||
/*
|
||||
* The defaults come from config options but can be overriden by module
|
||||
* or bootarg options.
|
||||
*/
|
||||
static int perdev_minors = CONFIG_MMC_BLOCK_MINORS;
|
||||
|
||||
/*
|
||||
* We've only got one major, so number of mmcblk devices is
|
||||
* limited to 256 / number of minors per device.
|
||||
*/
|
||||
static int max_devices;
|
||||
|
||||
/* 256 minors, so at most 256 separate devices */
|
||||
static DECLARE_BITMAP(dev_use, 256);
|
||||
|
||||
/*
|
||||
* There is one mmc_blk_data per slot.
|
||||
|
@ -67,6 +79,9 @@ struct mmc_blk_data {
|
|||
|
||||
static DEFINE_MUTEX(open_lock);
|
||||
|
||||
module_param(perdev_minors, int, 0444);
|
||||
MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
|
||||
|
||||
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
|
@ -88,10 +103,10 @@ static void mmc_blk_put(struct mmc_blk_data *md)
|
|||
md->usage--;
|
||||
if (md->usage == 0) {
|
||||
int devmaj = MAJOR(disk_devt(md->disk));
|
||||
int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT;
|
||||
int devidx = MINOR(disk_devt(md->disk)) / perdev_minors;
|
||||
|
||||
if (!devmaj)
|
||||
devidx = md->disk->first_minor >> MMC_SHIFT;
|
||||
devidx = md->disk->first_minor / perdev_minors;
|
||||
|
||||
blk_cleanup_queue(md->queue.queue);
|
||||
|
||||
|
@ -373,7 +388,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
readcmd = MMC_READ_SINGLE_BLOCK;
|
||||
writecmd = MMC_WRITE_BLOCK;
|
||||
}
|
||||
|
||||
if (rq_data_dir(req) == READ) {
|
||||
brq.cmd.opcode = readcmd;
|
||||
brq.data.flags |= MMC_DATA_READ;
|
||||
|
@ -567,8 +581,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
struct mmc_blk_data *md;
|
||||
int devidx, ret;
|
||||
|
||||
devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
|
||||
if (devidx >= MMC_NUM_MINORS)
|
||||
devidx = find_first_zero_bit(dev_use, max_devices);
|
||||
if (devidx >= max_devices)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
__set_bit(devidx, dev_use);
|
||||
|
||||
|
@ -585,7 +599,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
*/
|
||||
md->read_only = mmc_blk_readonly(card);
|
||||
|
||||
md->disk = alloc_disk(1 << MMC_SHIFT);
|
||||
md->disk = alloc_disk(perdev_minors);
|
||||
if (md->disk == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_kfree;
|
||||
|
@ -602,7 +616,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
md->queue.data = md;
|
||||
|
||||
md->disk->major = MMC_BLOCK_MAJOR;
|
||||
md->disk->first_minor = devidx << MMC_SHIFT;
|
||||
md->disk->first_minor = devidx * perdev_minors;
|
||||
md->disk->fops = &mmc_bdops;
|
||||
md->disk->private_data = md;
|
||||
md->disk->queue = md->queue.queue;
|
||||
|
@ -620,7 +634,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
* messages to tell when the card is present.
|
||||
*/
|
||||
|
||||
sprintf(md->disk->disk_name, "mmcblk%d", devidx);
|
||||
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
|
||||
"mmcblk%d", devidx);
|
||||
|
||||
blk_queue_logical_block_size(md->queue.queue, 512);
|
||||
|
||||
|
@ -651,23 +666,15 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
static int
|
||||
mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
int err;
|
||||
|
||||
/* Block-addressed cards ignore MMC_SET_BLOCKLEN. */
|
||||
if (mmc_card_blockaddr(card))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.arg = 512;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
err = mmc_set_blocklen(card, 512);
|
||||
mmc_release_host(card->host);
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
|
||||
md->disk->disk_name, cmd.arg, err);
|
||||
printk(KERN_ERR "%s: unable to set block size to 512: %d\n",
|
||||
md->disk->disk_name, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -678,7 +685,6 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||
{
|
||||
struct mmc_blk_data *md;
|
||||
int err;
|
||||
|
||||
char cap_str[10];
|
||||
|
||||
/*
|
||||
|
@ -768,6 +774,11 @@ static int __init mmc_blk_init(void)
|
|||
{
|
||||
int res;
|
||||
|
||||
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
|
||||
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
|
||||
|
||||
max_devices = 256 / perdev_minors;
|
||||
|
||||
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
||||
if (res)
|
||||
goto out;
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/swap.h> /* For nr_free_buffer_pages() */
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#define RESULT_OK 0
|
||||
#define RESULT_FAIL 1
|
||||
|
@ -56,7 +61,9 @@ struct mmc_test_mem {
|
|||
* struct mmc_test_area - information for performance tests.
|
||||
* @max_sz: test area size (in bytes)
|
||||
* @dev_addr: address on card at which to do performance tests
|
||||
* @max_segs: maximum segments in scatterlist @sg
|
||||
* @max_tfr: maximum transfer size allowed by driver (in bytes)
|
||||
* @max_segs: maximum segments allowed by driver in scatterlist @sg
|
||||
* @max_seg_sz: maximum segment size allowed by driver
|
||||
* @blocks: number of (512 byte) blocks currently mapped by @sg
|
||||
* @sg_len: length of currently mapped scatterlist @sg
|
||||
* @mem: allocated memory
|
||||
|
@ -65,13 +72,59 @@ struct mmc_test_mem {
|
|||
struct mmc_test_area {
|
||||
unsigned long max_sz;
|
||||
unsigned int dev_addr;
|
||||
unsigned int max_tfr;
|
||||
unsigned int max_segs;
|
||||
unsigned int max_seg_sz;
|
||||
unsigned int blocks;
|
||||
unsigned int sg_len;
|
||||
struct mmc_test_mem *mem;
|
||||
struct scatterlist *sg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_transfer_result - transfer results for performance tests.
|
||||
* @link: double-linked list
|
||||
* @count: amount of group of sectors to check
|
||||
* @sectors: amount of sectors to check in one group
|
||||
* @ts: time values of transfer
|
||||
* @rate: calculated transfer rate
|
||||
*/
|
||||
struct mmc_test_transfer_result {
|
||||
struct list_head link;
|
||||
unsigned int count;
|
||||
unsigned int sectors;
|
||||
struct timespec ts;
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_general_result - results for tests.
|
||||
* @link: double-linked list
|
||||
* @card: card under test
|
||||
* @testcase: number of test case
|
||||
* @result: result of test run
|
||||
* @tr_lst: transfer measurements if any as mmc_test_transfer_result
|
||||
*/
|
||||
struct mmc_test_general_result {
|
||||
struct list_head link;
|
||||
struct mmc_card *card;
|
||||
int testcase;
|
||||
int result;
|
||||
struct list_head tr_lst;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_dbgfs_file - debugfs related file.
|
||||
* @link: double-linked list
|
||||
* @card: card under test
|
||||
* @file: file created under debugfs
|
||||
*/
|
||||
struct mmc_test_dbgfs_file {
|
||||
struct list_head link;
|
||||
struct mmc_card *card;
|
||||
struct dentry *file;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_card - test information.
|
||||
* @card: card under test
|
||||
|
@ -79,6 +132,7 @@ struct mmc_test_area {
|
|||
* @buffer: transfer buffer
|
||||
* @highmem: buffer for highmem tests
|
||||
* @area: information for performance tests
|
||||
* @gr: pointer to results of current testcase
|
||||
*/
|
||||
struct mmc_test_card {
|
||||
struct mmc_card *card;
|
||||
|
@ -88,7 +142,8 @@ struct mmc_test_card {
|
|||
#ifdef CONFIG_HIGHMEM
|
||||
struct page *highmem;
|
||||
#endif
|
||||
struct mmc_test_area area;
|
||||
struct mmc_test_area area;
|
||||
struct mmc_test_general_result *gr;
|
||||
};
|
||||
|
||||
/*******************************************************************/
|
||||
|
@ -100,17 +155,7 @@ struct mmc_test_card {
|
|||
*/
|
||||
static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
int ret;
|
||||
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.arg = size;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return mmc_set_blocklen(test->card, size);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -245,27 +290,38 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem)
|
|||
|
||||
/*
|
||||
* Allocate a lot of memory, preferrably max_sz but at least min_sz. In case
|
||||
* there isn't much memory do not exceed 1/16th total lowmem pages.
|
||||
* there isn't much memory do not exceed 1/16th total lowmem pages. Also do
|
||||
* not exceed a maximum number of segments and try not to make segments much
|
||||
* bigger than maximum segment size.
|
||||
*/
|
||||
static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
|
||||
unsigned long max_sz)
|
||||
unsigned long max_sz,
|
||||
unsigned int max_segs,
|
||||
unsigned int max_seg_sz)
|
||||
{
|
||||
unsigned long max_page_cnt = DIV_ROUND_UP(max_sz, PAGE_SIZE);
|
||||
unsigned long min_page_cnt = DIV_ROUND_UP(min_sz, PAGE_SIZE);
|
||||
unsigned long max_seg_page_cnt = DIV_ROUND_UP(max_seg_sz, PAGE_SIZE);
|
||||
unsigned long page_cnt = 0;
|
||||
unsigned long limit = nr_free_buffer_pages() >> 4;
|
||||
struct mmc_test_mem *mem;
|
||||
|
||||
if (max_page_cnt > limit)
|
||||
max_page_cnt = limit;
|
||||
if (max_page_cnt < min_page_cnt)
|
||||
max_page_cnt = min_page_cnt;
|
||||
if (min_page_cnt > max_page_cnt)
|
||||
min_page_cnt = max_page_cnt;
|
||||
|
||||
if (max_seg_page_cnt > max_page_cnt)
|
||||
max_seg_page_cnt = max_page_cnt;
|
||||
|
||||
if (max_segs > max_page_cnt)
|
||||
max_segs = max_page_cnt;
|
||||
|
||||
mem = kzalloc(sizeof(struct mmc_test_mem), GFP_KERNEL);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_page_cnt,
|
||||
mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_segs,
|
||||
GFP_KERNEL);
|
||||
if (!mem->arr)
|
||||
goto out_free;
|
||||
|
@ -276,7 +332,7 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
|
|||
gfp_t flags = GFP_KERNEL | GFP_DMA | __GFP_NOWARN |
|
||||
__GFP_NORETRY;
|
||||
|
||||
order = get_order(max_page_cnt << PAGE_SHIFT);
|
||||
order = get_order(max_seg_page_cnt << PAGE_SHIFT);
|
||||
while (1) {
|
||||
page = alloc_pages(flags, order);
|
||||
if (page || !order)
|
||||
|
@ -295,6 +351,11 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
|
|||
break;
|
||||
max_page_cnt -= 1UL << order;
|
||||
page_cnt += 1UL << order;
|
||||
if (mem->cnt >= max_segs) {
|
||||
if (page_cnt < min_page_cnt)
|
||||
goto out_free;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mem;
|
||||
|
@ -310,7 +371,8 @@ out_free:
|
|||
*/
|
||||
static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz,
|
||||
struct scatterlist *sglist, int repeat,
|
||||
unsigned int max_segs, unsigned int *sg_len)
|
||||
unsigned int max_segs, unsigned int max_seg_sz,
|
||||
unsigned int *sg_len)
|
||||
{
|
||||
struct scatterlist *sg = NULL;
|
||||
unsigned int i;
|
||||
|
@ -322,8 +384,10 @@ static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz,
|
|||
for (i = 0; i < mem->cnt; i++) {
|
||||
unsigned long len = PAGE_SIZE << mem->arr[i].order;
|
||||
|
||||
if (sz < len)
|
||||
if (len > sz)
|
||||
len = sz;
|
||||
if (len > max_seg_sz)
|
||||
len = max_seg_sz;
|
||||
if (sg)
|
||||
sg = sg_next(sg);
|
||||
else
|
||||
|
@ -355,6 +419,7 @@ static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
|
|||
unsigned long sz,
|
||||
struct scatterlist *sglist,
|
||||
unsigned int max_segs,
|
||||
unsigned int max_seg_sz,
|
||||
unsigned int *sg_len)
|
||||
{
|
||||
struct scatterlist *sg = NULL;
|
||||
|
@ -365,7 +430,7 @@ static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
|
|||
sg_init_table(sglist, max_segs);
|
||||
|
||||
*sg_len = 0;
|
||||
while (sz && i) {
|
||||
while (sz) {
|
||||
base = page_address(mem->arr[--i].page);
|
||||
cnt = 1 << mem->arr[i].order;
|
||||
while (sz && cnt) {
|
||||
|
@ -374,7 +439,9 @@ static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
|
|||
continue;
|
||||
last_addr = addr;
|
||||
len = PAGE_SIZE;
|
||||
if (sz < len)
|
||||
if (len > max_seg_sz)
|
||||
len = max_seg_sz;
|
||||
if (len > sz)
|
||||
len = sz;
|
||||
if (sg)
|
||||
sg = sg_next(sg);
|
||||
|
@ -386,6 +453,8 @@ static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
|
|||
sz -= len;
|
||||
*sg_len += 1;
|
||||
}
|
||||
if (i == 0)
|
||||
i = mem->cnt;
|
||||
}
|
||||
|
||||
if (sg)
|
||||
|
@ -420,6 +489,30 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts)
|
|||
return bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save transfer results for future usage
|
||||
*/
|
||||
static void mmc_test_save_transfer_result(struct mmc_test_card *test,
|
||||
unsigned int count, unsigned int sectors, struct timespec ts,
|
||||
unsigned int rate)
|
||||
{
|
||||
struct mmc_test_transfer_result *tr;
|
||||
|
||||
if (!test->gr)
|
||||
return;
|
||||
|
||||
tr = kmalloc(sizeof(struct mmc_test_transfer_result), GFP_KERNEL);
|
||||
if (!tr)
|
||||
return;
|
||||
|
||||
tr->count = count;
|
||||
tr->sectors = sectors;
|
||||
tr->ts = ts;
|
||||
tr->rate = rate;
|
||||
|
||||
list_add_tail(&tr->link, &test->gr->tr_lst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the transfer rate.
|
||||
*/
|
||||
|
@ -436,8 +529,10 @@ static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
|
|||
printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
|
||||
"seconds (%u kB/s, %u KiB/s)\n",
|
||||
mmc_hostname(test->card->host), sectors, sectors >> 1,
|
||||
(sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
|
||||
(sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
|
||||
(unsigned long)ts.tv_nsec, rate / 1000, rate / 1024);
|
||||
|
||||
mmc_test_save_transfer_result(test, 1, sectors, ts, rate);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -458,9 +553,11 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
|
|||
printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
|
||||
"%lu.%09lu seconds (%u kB/s, %u KiB/s)\n",
|
||||
mmc_hostname(test->card->host), count, sectors, count,
|
||||
sectors >> 1, (sectors == 1 ? ".5" : ""),
|
||||
sectors >> 1, (sectors & 1 ? ".5" : ""),
|
||||
(unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec,
|
||||
rate / 1000, rate / 1024);
|
||||
|
||||
mmc_test_save_transfer_result(test, count, sectors, ts, rate);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1215,16 +1312,22 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
|
|||
int max_scatter)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
int err;
|
||||
|
||||
t->blocks = sz >> 9;
|
||||
|
||||
if (max_scatter) {
|
||||
return mmc_test_map_sg_max_scatter(t->mem, sz, t->sg,
|
||||
t->max_segs, &t->sg_len);
|
||||
} else {
|
||||
return mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs,
|
||||
err = mmc_test_map_sg_max_scatter(t->mem, sz, t->sg,
|
||||
t->max_segs, t->max_seg_sz,
|
||||
&t->sg_len);
|
||||
} else {
|
||||
err = mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs,
|
||||
t->max_seg_sz, &t->sg_len);
|
||||
}
|
||||
if (err)
|
||||
printk(KERN_INFO "%s: Failed to map sg list\n",
|
||||
mmc_hostname(test->card->host));
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1249,6 +1352,22 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
|||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* In the case of a maximally scattered transfer, the maximum transfer
|
||||
* size is further limited by using PAGE_SIZE segments.
|
||||
*/
|
||||
if (max_scatter) {
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long max_tfr;
|
||||
|
||||
if (t->max_seg_sz >= PAGE_SIZE)
|
||||
max_tfr = t->max_segs * PAGE_SIZE;
|
||||
else
|
||||
max_tfr = t->max_segs * t->max_seg_sz;
|
||||
if (sz > max_tfr)
|
||||
sz = max_tfr;
|
||||
}
|
||||
|
||||
ret = mmc_test_area_map(test, sz, max_scatter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1274,7 +1393,7 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
|||
*/
|
||||
static int mmc_test_area_fill(struct mmc_test_card *test)
|
||||
{
|
||||
return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr,
|
||||
return mmc_test_area_io(test, test->area.max_tfr, test->area.dev_addr,
|
||||
1, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -1328,16 +1447,29 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
|||
t->max_sz = TEST_AREA_MAX_SIZE;
|
||||
else
|
||||
t->max_sz = (unsigned long)test->card->pref_erase << 9;
|
||||
|
||||
t->max_segs = test->card->host->max_segs;
|
||||
t->max_seg_sz = test->card->host->max_seg_size;
|
||||
|
||||
t->max_tfr = t->max_sz;
|
||||
if (t->max_tfr >> 9 > test->card->host->max_blk_count)
|
||||
t->max_tfr = test->card->host->max_blk_count << 9;
|
||||
if (t->max_tfr > test->card->host->max_req_size)
|
||||
t->max_tfr = test->card->host->max_req_size;
|
||||
if (t->max_tfr / t->max_seg_sz > t->max_segs)
|
||||
t->max_tfr = t->max_segs * t->max_seg_sz;
|
||||
|
||||
/*
|
||||
* Try to allocate enough memory for the whole area. Less is OK
|
||||
* Try to allocate enough memory for a max. sized transfer. Less is OK
|
||||
* because the same memory can be mapped into the scatterlist more than
|
||||
* once.
|
||||
* once. Also, take into account the limits imposed on scatterlist
|
||||
* segments by the host driver.
|
||||
*/
|
||||
t->mem = mmc_test_alloc_mem(min_sz, t->max_sz);
|
||||
t->mem = mmc_test_alloc_mem(min_sz, t->max_tfr, t->max_segs,
|
||||
t->max_seg_sz);
|
||||
if (!t->mem)
|
||||
return -ENOMEM;
|
||||
|
||||
t->max_segs = DIV_ROUND_UP(t->max_sz, PAGE_SIZE);
|
||||
t->sg = kmalloc(sizeof(struct scatterlist) * t->max_segs, GFP_KERNEL);
|
||||
if (!t->sg) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -1401,7 +1533,7 @@ static int mmc_test_area_prepare_fill(struct mmc_test_card *test)
|
|||
static int mmc_test_best_performance(struct mmc_test_card *test, int write,
|
||||
int max_scatter)
|
||||
{
|
||||
return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr,
|
||||
return mmc_test_area_io(test, test->area.max_tfr, test->area.dev_addr,
|
||||
write, max_scatter, 1);
|
||||
}
|
||||
|
||||
|
@ -1446,12 +1578,13 @@ static int mmc_test_profile_read_perf(struct mmc_test_card *test)
|
|||
unsigned int dev_addr;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
dev_addr = test->area.dev_addr + (sz >> 9);
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sz = test->area.max_tfr;
|
||||
dev_addr = test->area.dev_addr;
|
||||
return mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
|
||||
}
|
||||
|
@ -1468,7 +1601,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
|||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
dev_addr = test->area.dev_addr + (sz >> 9);
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
|
||||
if (ret)
|
||||
|
@ -1477,6 +1610,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
|||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
sz = test->area.max_tfr;
|
||||
dev_addr = test->area.dev_addr;
|
||||
return mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
|
||||
}
|
||||
|
@ -1516,29 +1650,63 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz)
|
||||
{
|
||||
unsigned int dev_addr, i, cnt;
|
||||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
}
|
||||
getnstimeofday(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Consecutive read performance by transfer size.
|
||||
*/
|
||||
static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned long sz;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
ret = mmc_test_seq_read_perf(test, sz);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sz = test->area.max_tfr;
|
||||
return mmc_test_seq_read_perf(test, sz);
|
||||
}
|
||||
|
||||
static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
|
||||
{
|
||||
unsigned int dev_addr, i, cnt;
|
||||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
}
|
||||
getnstimeofday(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
}
|
||||
getnstimeofday(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1548,27 +1716,15 @@ static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test)
|
|||
static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test)
|
||||
{
|
||||
unsigned long sz;
|
||||
unsigned int dev_addr, i, cnt;
|
||||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
|
||||
ret = mmc_test_area_erase(test);
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
ret = mmc_test_seq_write_perf(test, sz);
|
||||
if (ret)
|
||||
return ret;
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_addr += (sz >> 9);
|
||||
}
|
||||
getnstimeofday(&ts2);
|
||||
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
|
||||
}
|
||||
return 0;
|
||||
sz = test->area.max_tfr;
|
||||
return mmc_test_seq_write_perf(test, sz);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1853,6 +2009,8 @@ static const struct mmc_test_case mmc_test_cases[] = {
|
|||
|
||||
static DEFINE_MUTEX(mmc_test_lock);
|
||||
|
||||
static LIST_HEAD(mmc_test_result);
|
||||
|
||||
static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||
{
|
||||
int i, ret;
|
||||
|
@ -1863,6 +2021,8 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_claim_host(test->card->host);
|
||||
|
||||
for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
|
||||
struct mmc_test_general_result *gr;
|
||||
|
||||
if (testcase && ((i + 1) != testcase))
|
||||
continue;
|
||||
|
||||
|
@ -1881,6 +2041,25 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
}
|
||||
}
|
||||
|
||||
gr = kzalloc(sizeof(struct mmc_test_general_result),
|
||||
GFP_KERNEL);
|
||||
if (gr) {
|
||||
INIT_LIST_HEAD(&gr->tr_lst);
|
||||
|
||||
/* Assign data what we know already */
|
||||
gr->card = test->card;
|
||||
gr->testcase = i;
|
||||
|
||||
/* Append container to global one */
|
||||
list_add_tail(&gr->link, &mmc_test_result);
|
||||
|
||||
/*
|
||||
* Save the pointer to created container in our private
|
||||
* structure.
|
||||
*/
|
||||
test->gr = gr;
|
||||
}
|
||||
|
||||
ret = mmc_test_cases[i].run(test);
|
||||
switch (ret) {
|
||||
case RESULT_OK:
|
||||
|
@ -1906,6 +2085,10 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_hostname(test->card->host), ret);
|
||||
}
|
||||
|
||||
/* Save the result */
|
||||
if (gr)
|
||||
gr->result = ret;
|
||||
|
||||
if (mmc_test_cases[i].cleanup) {
|
||||
ret = mmc_test_cases[i].cleanup(test);
|
||||
if (ret) {
|
||||
|
@ -1923,30 +2106,95 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_hostname(test->card->host));
|
||||
}
|
||||
|
||||
static ssize_t mmc_test_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static void mmc_test_free_result(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_test_general_result *gr, *grs;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
list_for_each_entry_safe(gr, grs, &mmc_test_result, link) {
|
||||
struct mmc_test_transfer_result *tr, *trs;
|
||||
|
||||
if (card && gr->card != card)
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(tr, trs, &gr->tr_lst, link) {
|
||||
list_del(&tr->link);
|
||||
kfree(tr);
|
||||
}
|
||||
|
||||
list_del(&gr->link);
|
||||
kfree(gr);
|
||||
}
|
||||
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
}
|
||||
|
||||
static LIST_HEAD(mmc_test_file_test);
|
||||
|
||||
static int mtf_test_show(struct seq_file *sf, void *data)
|
||||
{
|
||||
struct mmc_card *card = (struct mmc_card *)sf->private;
|
||||
struct mmc_test_general_result *gr;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
list_for_each_entry(gr, &mmc_test_result, link) {
|
||||
struct mmc_test_transfer_result *tr;
|
||||
|
||||
if (gr->card != card)
|
||||
continue;
|
||||
|
||||
seq_printf(sf, "Test %d: %d\n", gr->testcase + 1, gr->result);
|
||||
|
||||
list_for_each_entry(tr, &gr->tr_lst, link) {
|
||||
seq_printf(sf, "%u %d %lu.%09lu %u\n",
|
||||
tr->count, tr->sectors,
|
||||
(unsigned long)tr->ts.tv_sec,
|
||||
(unsigned long)tr->ts.tv_nsec,
|
||||
tr->rate);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mmc_test_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
static int mtf_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct mmc_card *card;
|
||||
return single_open(file, mtf_test_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t mtf_test_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct seq_file *sf = (struct seq_file *)file->private_data;
|
||||
struct mmc_card *card = (struct mmc_card *)sf->private;
|
||||
struct mmc_test_card *test;
|
||||
int testcase;
|
||||
char lbuf[12];
|
||||
long testcase;
|
||||
|
||||
card = container_of(dev, struct mmc_card, dev);
|
||||
if (count >= sizeof(lbuf))
|
||||
return -EINVAL;
|
||||
|
||||
testcase = simple_strtol(buf, NULL, 10);
|
||||
if (copy_from_user(lbuf, buf, count))
|
||||
return -EFAULT;
|
||||
lbuf[count] = '\0';
|
||||
|
||||
if (strict_strtol(lbuf, 10, &testcase))
|
||||
return -EINVAL;
|
||||
|
||||
test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
|
||||
if (!test)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Remove all test cases associated with given card. Thus we have only
|
||||
* actual data of the last run.
|
||||
*/
|
||||
mmc_test_free_result(card);
|
||||
|
||||
test->card = card;
|
||||
|
||||
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
|
||||
|
@ -1973,16 +2221,78 @@ static ssize_t mmc_test_store(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(test, S_IWUSR | S_IRUGO, mmc_test_show, mmc_test_store);
|
||||
static const struct file_operations mmc_test_fops_test = {
|
||||
.open = mtf_test_open,
|
||||
.read = seq_read,
|
||||
.write = mtf_test_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void mmc_test_free_file_test(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_test_dbgfs_file *df, *dfs;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
list_for_each_entry_safe(df, dfs, &mmc_test_file_test, link) {
|
||||
if (card && df->card != card)
|
||||
continue;
|
||||
debugfs_remove(df->file);
|
||||
list_del(&df->link);
|
||||
kfree(df);
|
||||
}
|
||||
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
}
|
||||
|
||||
static int mmc_test_register_file_test(struct mmc_card *card)
|
||||
{
|
||||
struct dentry *file = NULL;
|
||||
struct mmc_test_dbgfs_file *df;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
if (card->debugfs_root)
|
||||
file = debugfs_create_file("test", S_IWUSR | S_IRUGO,
|
||||
card->debugfs_root, card, &mmc_test_fops_test);
|
||||
|
||||
if (IS_ERR_OR_NULL(file)) {
|
||||
dev_err(&card->dev,
|
||||
"Can't create file. Perhaps debugfs is disabled.\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
df = kmalloc(sizeof(struct mmc_test_dbgfs_file), GFP_KERNEL);
|
||||
if (!df) {
|
||||
debugfs_remove(file);
|
||||
dev_err(&card->dev,
|
||||
"Can't allocate memory for internal usage.\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
df->card = card;
|
||||
df->file = file;
|
||||
|
||||
list_add(&df->link, &mmc_test_file_test);
|
||||
|
||||
err:
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_test_probe(struct mmc_card *card)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((card->type != MMC_TYPE_MMC) && (card->type != MMC_TYPE_SD))
|
||||
if (!mmc_card_mmc(card) && !mmc_card_sd(card))
|
||||
return -ENODEV;
|
||||
|
||||
ret = device_create_file(&card->dev, &dev_attr_test);
|
||||
ret = mmc_test_register_file_test(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1993,7 +2303,8 @@ static int mmc_test_probe(struct mmc_card *card)
|
|||
|
||||
static void mmc_test_remove(struct mmc_card *card)
|
||||
{
|
||||
device_remove_file(&card->dev, &dev_attr_test);
|
||||
mmc_test_free_result(card);
|
||||
mmc_test_free_file_test(card);
|
||||
}
|
||||
|
||||
static struct mmc_driver mmc_driver = {
|
||||
|
@ -2011,6 +2322,10 @@ static int __init mmc_test_init(void)
|
|||
|
||||
static void __exit mmc_test_exit(void)
|
||||
{
|
||||
/* Clear stalled data if card is still plugged */
|
||||
mmc_test_free_result(NULL);
|
||||
mmc_test_free_file_test(NULL);
|
||||
|
||||
mmc_unregister_driver(&mmc_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MMC_BLOCK_BOUNCE
|
||||
if (host->max_hw_segs == 1) {
|
||||
if (host->max_segs == 1) {
|
||||
unsigned int bouncesz;
|
||||
|
||||
bouncesz = MMC_QUEUE_BOUNCESZ;
|
||||
|
@ -196,21 +196,23 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
|
|||
blk_queue_bounce_limit(mq->queue, limit);
|
||||
blk_queue_max_hw_sectors(mq->queue,
|
||||
min(host->max_blk_count, host->max_req_size / 512));
|
||||
blk_queue_max_segments(mq->queue, host->max_hw_segs);
|
||||
blk_queue_max_segments(mq->queue, host->max_segs);
|
||||
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
|
||||
|
||||
mq->sg = kmalloc(sizeof(struct scatterlist) *
|
||||
host->max_phys_segs, GFP_KERNEL);
|
||||
host->max_segs, GFP_KERNEL);
|
||||
if (!mq->sg) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup_queue;
|
||||
}
|
||||
sg_init_table(mq->sg, host->max_phys_segs);
|
||||
sg_init_table(mq->sg, host->max_segs);
|
||||
}
|
||||
|
||||
init_MUTEX(&mq->thread_sem);
|
||||
sema_init(&mq->thread_sem, 1);
|
||||
|
||||
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d",
|
||||
host->index);
|
||||
|
||||
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
|
||||
if (IS_ERR(mq->thread)) {
|
||||
ret = PTR_ERR(mq->thread);
|
||||
goto free_bounce_sg;
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
# 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 \
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
@ -22,13 +23,12 @@
|
|||
#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);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
switch (card->type) {
|
||||
case MMC_TYPE_MMC:
|
||||
|
@ -62,7 +62,7 @@ static int mmc_bus_match(struct device *dev, struct device_driver *drv)
|
|||
static int
|
||||
mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
const char *type;
|
||||
int retval = 0;
|
||||
|
||||
|
@ -105,7 +105,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
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);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return drv->probe(card);
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ static int mmc_bus_probe(struct device *dev)
|
|||
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);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
drv->remove(card);
|
||||
|
||||
|
@ -123,7 +123,7 @@ static int mmc_bus_remove(struct device *dev)
|
|||
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);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && drv->suspend)
|
||||
|
@ -134,7 +134,7 @@ static int mmc_bus_suspend(struct device *dev, pm_message_t state)
|
|||
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);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && drv->resume)
|
||||
|
@ -142,6 +142,41 @@ static int mmc_bus_resume(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int mmc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return mmc_power_save_host(card->host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
return mmc_power_restore_host(card->host);
|
||||
}
|
||||
|
||||
static int mmc_runtime_idle(struct device *dev)
|
||||
{
|
||||
return pm_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mmc_bus_pm_ops = {
|
||||
.runtime_suspend = mmc_runtime_suspend,
|
||||
.runtime_resume = mmc_runtime_resume,
|
||||
.runtime_idle = mmc_runtime_idle,
|
||||
};
|
||||
|
||||
#define MMC_PM_OPS_PTR (&mmc_bus_pm_ops)
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
#define MMC_PM_OPS_PTR NULL
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static struct bus_type mmc_bus_type = {
|
||||
.name = "mmc",
|
||||
.dev_attrs = mmc_dev_attrs,
|
||||
|
@ -151,6 +186,7 @@ static struct bus_type mmc_bus_type = {
|
|||
.remove = mmc_bus_remove,
|
||||
.suspend = mmc_bus_suspend,
|
||||
.resume = mmc_bus_resume,
|
||||
.pm = MMC_PM_OPS_PTR,
|
||||
};
|
||||
|
||||
int mmc_register_bus(void)
|
||||
|
@ -189,7 +225,7 @@ EXPORT_SYMBOL(mmc_unregister_driver);
|
|||
|
||||
static void mmc_release_card(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
sdio_free_common_cis(card);
|
||||
|
||||
|
@ -254,14 +290,16 @@ int mmc_add_card(struct mmc_card *card)
|
|||
}
|
||||
|
||||
if (mmc_host_is_spi(card->host)) {
|
||||
printk(KERN_INFO "%s: new %s%s card on SPI\n",
|
||||
printk(KERN_INFO "%s: new %s%s%s card on SPI\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
type);
|
||||
} else {
|
||||
printk(KERN_INFO "%s: new %s%s card at address %04x\n",
|
||||
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
type, card->rca);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#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); \
|
||||
struct mmc_card *card = mmc_dev_to_card(dev); \
|
||||
return sprintf(buf, fmt, args); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
|
||||
|
|
|
@ -58,6 +58,7 @@ int mmc_assume_removable;
|
|||
#else
|
||||
int mmc_assume_removable = 1;
|
||||
#endif
|
||||
EXPORT_SYMBOL(mmc_assume_removable);
|
||||
module_param_named(removable, mmc_assume_removable, bool, 0644);
|
||||
MODULE_PARM_DESC(
|
||||
removable,
|
||||
|
@ -649,13 +650,23 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
|
|||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change data bus width and DDR mode of a host.
|
||||
*/
|
||||
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
|
||||
unsigned int ddr)
|
||||
{
|
||||
host->ios.bus_width = width;
|
||||
host->ios.ddr = ddr;
|
||||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change data bus width of a host.
|
||||
*/
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
|
||||
{
|
||||
host->ios.bus_width = width;
|
||||
mmc_set_ios(host);
|
||||
mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -771,8 +782,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
|
|||
|
||||
/**
|
||||
* mmc_regulator_set_ocr - set regulator to match host->ios voltage
|
||||
* @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
|
||||
* @mmc: the host to regulate
|
||||
* @supply: regulator to use
|
||||
* @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
|
@ -780,15 +792,12 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
|
|||
* a particular supply voltage. This would normally be called from the
|
||||
* set_ios() method.
|
||||
*/
|
||||
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit)
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit)
|
||||
{
|
||||
int result = 0;
|
||||
int min_uV, max_uV;
|
||||
int enabled;
|
||||
|
||||
enabled = regulator_is_enabled(supply);
|
||||
if (enabled < 0)
|
||||
return enabled;
|
||||
|
||||
if (vdd_bit) {
|
||||
int tmp;
|
||||
|
@ -819,17 +828,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit)
|
|||
else
|
||||
result = 0;
|
||||
|
||||
if (result == 0 && !enabled)
|
||||
if (result == 0 && !mmc->regulator_enabled) {
|
||||
result = regulator_enable(supply);
|
||||
} else if (enabled) {
|
||||
if (!result)
|
||||
mmc->regulator_enabled = true;
|
||||
}
|
||||
} else if (mmc->regulator_enabled) {
|
||||
result = regulator_disable(supply);
|
||||
if (result == 0)
|
||||
mmc->regulator_enabled = false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
dev_err(mmc_dev(mmc),
|
||||
"could not set regulator OCR (%d)\n", result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_regulator_set_ocr);
|
||||
|
||||
#endif
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
/*
|
||||
* Mask off any voltages we don't support and select
|
||||
|
@ -907,12 +924,7 @@ static void mmc_power_up(struct mmc_host *host)
|
|||
*/
|
||||
mmc_delay(10);
|
||||
|
||||
if (host->f_min > 400000) {
|
||||
pr_warning("%s: Minimum clock frequency too high for "
|
||||
"identification mode\n", mmc_hostname(host));
|
||||
host->ios.clock = host->f_min;
|
||||
} else
|
||||
host->ios.clock = 400000;
|
||||
host->ios.clock = host->f_init;
|
||||
|
||||
host->ios.power_mode = MMC_POWER_ON;
|
||||
mmc_set_ios(host);
|
||||
|
@ -1397,6 +1409,21 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_erase_group_aligned);
|
||||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
|
||||
if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card))
|
||||
return 0;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.arg = blocklen;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
return mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_set_blocklen);
|
||||
|
||||
void mmc_rescan(struct work_struct *work)
|
||||
{
|
||||
struct mmc_host *host =
|
||||
|
@ -1404,6 +1431,8 @@ void mmc_rescan(struct work_struct *work)
|
|||
u32 ocr;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
|
@ -1443,55 +1472,71 @@ void mmc_rescan(struct work_struct *work)
|
|||
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
|
||||
goto out;
|
||||
|
||||
mmc_claim_host(host);
|
||||
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_power_up(host);
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
if (freqs[i] >= host->f_min)
|
||||
host->f_init = freqs[i];
|
||||
else if (!i || freqs[i-1] > host->f_min)
|
||||
host->f_init = host->f_min;
|
||||
else {
|
||||
mmc_release_host(host);
|
||||
goto out;
|
||||
}
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
pr_info("%s: %s: trying to init card at %u Hz\n",
|
||||
mmc_hostname(host), __func__, host->f_init);
|
||||
#endif
|
||||
mmc_power_up(host);
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
|
||||
/*
|
||||
* First we search for SDIO...
|
||||
*/
|
||||
err = mmc_send_io_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sdio(host, ocr)) {
|
||||
mmc_claim_host(host);
|
||||
/* try SDMEM (but not MMC) even if SDIO is broken */
|
||||
if (mmc_send_app_op_cond(host, 0, &ocr))
|
||||
goto out_fail;
|
||||
/*
|
||||
* First we search for SDIO...
|
||||
*/
|
||||
err = mmc_send_io_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sdio(host, ocr)) {
|
||||
mmc_claim_host(host);
|
||||
/*
|
||||
* Try SDMEM (but not MMC) even if SDIO
|
||||
* is broken.
|
||||
*/
|
||||
if (mmc_send_app_op_cond(host, 0, &ocr))
|
||||
goto out_fail;
|
||||
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...then normal SD...
|
||||
*/
|
||||
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...then normal SD...
|
||||
*/
|
||||
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...and finally MMC.
|
||||
*/
|
||||
err = mmc_send_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_mmc(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* ...and finally MMC.
|
||||
*/
|
||||
err = mmc_send_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_mmc(host, ocr))
|
||||
mmc_power_off(host);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_fail:
|
||||
mmc_release_host(host);
|
||||
mmc_power_off(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
mmc_power_off(host);
|
||||
}
|
||||
out:
|
||||
if (host->caps & MMC_CAP_NEEDS_POLL)
|
||||
mmc_schedule_delayed_work(&host->detect, HZ);
|
||||
|
@ -1538,37 +1583,45 @@ void mmc_stop_host(struct mmc_host *host)
|
|||
mmc_power_off(host);
|
||||
}
|
||||
|
||||
void mmc_power_save_host(struct mmc_host *host)
|
||||
int mmc_power_save_host(struct mmc_host *host)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
|
||||
mmc_bus_put(host);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (host->bus_ops->power_save)
|
||||
host->bus_ops->power_save(host);
|
||||
ret = host->bus_ops->power_save(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
|
||||
mmc_power_off(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_power_save_host);
|
||||
|
||||
void mmc_power_restore_host(struct mmc_host *host)
|
||||
int mmc_power_restore_host(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
|
||||
mmc_bus_put(host);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mmc_power_up(host);
|
||||
host->bus_ops->power_restore(host);
|
||||
ret = host->bus_ops->power_restore(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_power_restore_host);
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ struct mmc_bus_ops {
|
|||
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 *);
|
||||
int (*power_save)(struct mmc_host *);
|
||||
int (*power_restore)(struct mmc_host *);
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
|
@ -35,6 +35,8 @@ 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);
|
||||
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
|
||||
unsigned int ddr);
|
||||
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
|
||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||
|
||||
|
@ -58,7 +60,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
|
|||
|
||||
/* Module parameters */
|
||||
extern int use_spi_crc;
|
||||
extern int mmc_assume_removable;
|
||||
|
||||
/* Debugfs information for hosts and cards */
|
||||
void mmc_add_host_debugfs(struct mmc_host *host);
|
||||
|
|
|
@ -134,6 +134,33 @@ static const struct file_operations mmc_ios_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static int mmc_clock_opt_get(void *data, u64 *val)
|
||||
{
|
||||
struct mmc_host *host = data;
|
||||
|
||||
*val = host->ios.clock;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_clock_opt_set(void *data, u64 val)
|
||||
{
|
||||
struct mmc_host *host = data;
|
||||
|
||||
/* We need this check due to input value is u64 */
|
||||
if (val > host->f_max)
|
||||
return -EINVAL;
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_set_clock(host, (unsigned int) val);
|
||||
mmc_release_host(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
|
||||
"%llu\n");
|
||||
|
||||
void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
{
|
||||
struct dentry *root;
|
||||
|
@ -150,11 +177,15 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
|||
host->debugfs_root = root;
|
||||
|
||||
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
|
||||
goto err_ios;
|
||||
goto err_node;
|
||||
|
||||
if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
&mmc_clock_fops))
|
||||
goto err_node;
|
||||
|
||||
return;
|
||||
|
||||
err_ios:
|
||||
err_node:
|
||||
debugfs_remove_recursive(root);
|
||||
host->debugfs_root = NULL;
|
||||
err_root:
|
||||
|
|
|
@ -94,8 +94,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
* 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_segs = 1;
|
||||
host->max_seg_size = PAGE_CACHE_SIZE;
|
||||
|
||||
host->max_req_size = PAGE_CACHE_SIZE;
|
||||
|
|
|
@ -258,6 +258,21 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
|||
}
|
||||
|
||||
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
|
||||
case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_52;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_DDR_1_2V | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_2V;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_8V;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
break;
|
||||
|
@ -360,7 +375,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
struct mmc_card *oldcard)
|
||||
{
|
||||
struct mmc_card *card;
|
||||
int err;
|
||||
int err, ddr = MMC_SDR_MODE;
|
||||
u32 cid[4];
|
||||
unsigned int max_dtr;
|
||||
|
||||
|
@ -503,17 +518,35 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
mmc_set_clock(host, max_dtr);
|
||||
|
||||
/*
|
||||
* Activate wide bus (if supported).
|
||||
* Indicate DDR mode (if supported).
|
||||
*/
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
||||
&& (host->caps & (MMC_CAP_1_8V_DDR)))
|
||||
ddr = MMC_1_8V_DDR_MODE;
|
||||
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
&& (host->caps & (MMC_CAP_1_2V_DDR)))
|
||||
ddr = MMC_1_2V_DDR_MODE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate wide bus and DDR (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;
|
||||
if (ddr)
|
||||
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
|
||||
else
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
|
||||
bus_width = MMC_BUS_WIDTH_8;
|
||||
} else {
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
|
||||
if (ddr)
|
||||
ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
|
||||
else
|
||||
ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
|
||||
bus_width = MMC_BUS_WIDTH_4;
|
||||
}
|
||||
|
||||
|
@ -524,12 +557,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
goto free_card;
|
||||
|
||||
if (err) {
|
||||
printk(KERN_WARNING "%s: switch to bus width %d "
|
||||
printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
|
||||
"failed\n", mmc_hostname(card->host),
|
||||
1 << bus_width);
|
||||
1 << bus_width, ddr);
|
||||
err = 0;
|
||||
} else {
|
||||
mmc_set_bus_width(card->host, bus_width);
|
||||
mmc_card_set_ddr_mode(card);
|
||||
mmc_set_bus_width_ddr(card->host, bus_width, ddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,12 +657,16 @@ static int mmc_resume(struct mmc_host *host)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void mmc_power_restore(struct mmc_host *host)
|
||||
static int mmc_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_claim_host(host);
|
||||
mmc_init_card(host, host->ocr, host->card);
|
||||
ret = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
|
@ -685,7 +723,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
|
|||
{
|
||||
const struct mmc_bus_ops *bus_ops;
|
||||
|
||||
if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
|
||||
if (!mmc_card_is_removable(host))
|
||||
bus_ops = &mmc_ops_unsafe;
|
||||
else
|
||||
bus_ops = &mmc_ops;
|
||||
|
|
|
@ -722,12 +722,16 @@ static int mmc_sd_resume(struct mmc_host *host)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void mmc_sd_power_restore(struct mmc_host *host)
|
||||
static int mmc_sd_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_claim_host(host);
|
||||
mmc_sd_init_card(host, host->ocr, host->card);
|
||||
ret = mmc_sd_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
|
@ -750,7 +754,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
|
|||
{
|
||||
const struct mmc_bus_ops *bus_ops;
|
||||
|
||||
if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
|
||||
if (!mmc_card_is_removable(host))
|
||||
bus_ops = &mmc_sd_ops_unsafe;
|
||||
else
|
||||
bus_ops = &mmc_sd_ops;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
@ -456,7 +457,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
return -ENOENT;
|
||||
|
||||
card = oldcard;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
|
@ -546,6 +546,11 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
|||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
/* Make sure card is powered before detecting it */
|
||||
err = pm_runtime_get_sync(&host->card->dev);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
/*
|
||||
|
@ -555,6 +560,7 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
|||
|
||||
mmc_release_host(host);
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
mmc_sdio_remove(host);
|
||||
|
||||
|
@ -562,6 +568,9 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
|||
mmc_detach_bus(host);
|
||||
mmc_release_host(host);
|
||||
}
|
||||
|
||||
/* Tell PM core that we're done */
|
||||
pm_runtime_put(&host->card->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -614,14 +623,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
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_4bit_bus(host->card);
|
||||
if (err > 0) {
|
||||
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
if (!err && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
mmc_release_host(host);
|
||||
|
@ -647,11 +648,29 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int mmc_sdio_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
(host->pm_flags & MMC_PM_KEEP_POWER));
|
||||
if (!ret && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,
|
||||
.power_restore = mmc_sdio_power_restore,
|
||||
};
|
||||
|
||||
|
||||
|
@ -698,6 +717,18 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|||
goto err;
|
||||
card = host->card;
|
||||
|
||||
/*
|
||||
* Let runtime PM core know our card is active
|
||||
*/
|
||||
err = pm_runtime_set_active(&card->dev);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Enable runtime PM for this card
|
||||
*/
|
||||
pm_runtime_enable(&card->dev);
|
||||
|
||||
/*
|
||||
* The number of functions on the card is encoded inside
|
||||
* the ocr.
|
||||
|
@ -712,6 +743,11 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|||
err = sdio_init_func(host->card, i + 1);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Enable Runtime PM for this func
|
||||
*/
|
||||
pm_runtime_enable(&card->sdio_func[i]->dev);
|
||||
}
|
||||
|
||||
mmc_release_host(host);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
|
@ -125,21 +126,46 @@ static int sdio_bus_probe(struct device *dev)
|
|||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
/* Unbound SDIO functions are always suspended.
|
||||
* During probe, the function is set active and the usage count
|
||||
* is incremented. If the driver supports runtime PM,
|
||||
* it should call pm_runtime_put_noidle() in its probe routine and
|
||||
* pm_runtime_get_noresume() in its remove routine.
|
||||
*/
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* 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;
|
||||
goto disable_runtimepm;
|
||||
|
||||
return drv->probe(func, id);
|
||||
ret = drv->probe(func, id);
|
||||
if (ret)
|
||||
goto disable_runtimepm;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_runtimepm:
|
||||
pm_runtime_put_noidle(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
/* Make sure card is powered before invoking ->remove() */
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
drv->remove(func);
|
||||
|
||||
|
@ -151,9 +177,63 @@ static int sdio_bus_remove(struct device *dev)
|
|||
sdio_release_host(func);
|
||||
}
|
||||
|
||||
/* First, undo the increment made directly above */
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
/* Then undo the runtime PM settings in sdio_bus_probe() */
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int sdio_bus_pm_prepare(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* Resume an SDIO device which was suspended at run time at this
|
||||
* point, in order to allow standard SDIO suspend/resume paths
|
||||
* to keep working as usual.
|
||||
*
|
||||
* Ultimately, the SDIO driver itself will decide (in its
|
||||
* suspend handler, or lack thereof) whether the card should be
|
||||
* removed or kept, and if kept, at what power state.
|
||||
*
|
||||
* At this point, PM core have increased our use count, so it's
|
||||
* safe to directly resume the device. After system is resumed
|
||||
* again, PM core will drop back its runtime PM use count, and if
|
||||
* needed device will be suspended again.
|
||||
*
|
||||
* The end result is guaranteed to be a power state that is
|
||||
* coherent with the device's runtime PM use count.
|
||||
*
|
||||
* The return value of pm_runtime_resume is deliberately unchecked
|
||||
* since there is little point in failing system suspend if a
|
||||
* device can't be resumed.
|
||||
*/
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(
|
||||
pm_generic_runtime_suspend,
|
||||
pm_generic_runtime_resume,
|
||||
pm_generic_runtime_idle
|
||||
)
|
||||
.prepare = sdio_bus_pm_prepare,
|
||||
};
|
||||
|
||||
#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops)
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
#define SDIO_PM_OPS_PTR NULL
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static struct bus_type sdio_bus_type = {
|
||||
.name = "sdio",
|
||||
.dev_attrs = sdio_dev_attrs,
|
||||
|
@ -161,6 +241,7 @@ static struct bus_type sdio_bus_type = {
|
|||
.uevent = sdio_bus_uevent,
|
||||
.probe = sdio_bus_probe,
|
||||
.remove = sdio_bus_remove,
|
||||
.pm = SDIO_PM_OPS_PTR,
|
||||
};
|
||||
|
||||
int sdio_register_bus(void)
|
||||
|
|
|
@ -130,6 +130,16 @@ config MMC_SDHCI_CNS3XXX
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_ESDHC_IMX
|
||||
bool "SDHCI platform support for the Freescale eSDHC i.MX controller"
|
||||
depends on MMC_SDHCI_PLTFM && (ARCH_MX25 || ARCH_MX35 || ARCH_MX5)
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Freescale eSDHC controller support on the platform
|
||||
bus, found on platforms like mx35/51.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_S3C
|
||||
tristate "SDHCI support on Samsung S3C SoC"
|
||||
depends on MMC_SDHCI && PLAT_SAMSUNG
|
||||
|
@ -145,6 +155,18 @@ config MMC_SDHCI_S3C
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_PXA
|
||||
tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support"
|
||||
depends on ARCH_PXA || ARCH_MMP
|
||||
select MMC_SDHCI
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller.
|
||||
If you have a PXA168/PXA910/MMP2 platform with SD Host Controller
|
||||
and a card slot, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_SPEAR
|
||||
tristate "SDHCI support on ST SPEAr platform"
|
||||
depends on MMC_SDHCI && PLAT_SPEAR
|
||||
|
@ -395,6 +417,7 @@ config MMC_TMIO
|
|||
config MMC_CB710
|
||||
tristate "ENE CB710 MMC/SD Interface support"
|
||||
depends on PCI
|
||||
select MISC_DEVICES
|
||||
select CB710_CORE
|
||||
help
|
||||
This option enables support for MMC/SD part of ENE CB710/720 Flash
|
||||
|
@ -451,3 +474,17 @@ config MMC_JZ4740
|
|||
SoCs.
|
||||
If you have a board based on such a SoC and with a SD/MMC slot,
|
||||
say Y or M here.
|
||||
|
||||
config MMC_USHC
|
||||
tristate "USB SD Host Controller (USHC) support"
|
||||
depends on USB
|
||||
help
|
||||
This selects support for USB SD Host Controllers based on
|
||||
the Cypress Astoria chip with firmware compliant with CSR's
|
||||
USB SD Host Controller specification (CS-118793-SP).
|
||||
|
||||
CSR boards with this device include: USB<>SDIO (M1985v2),
|
||||
and Ultrasira.
|
||||
|
||||
Note: These controllers only support SDIO cards and do not
|
||||
support MMC or SD memory cards.
|
||||
|
|
|
@ -2,16 +2,13 @@
|
|||
# Makefile for MMC/SD host controller drivers
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_MMC_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
|
||||
obj-$(CONFIG_MMC_PXA) += pxamci.o
|
||||
obj-$(CONFIG_MMC_IMX) += imxmmc.o
|
||||
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
|
||||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-pxa.o
|
||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||
|
@ -36,10 +33,12 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
|
|||
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
obj-$(CONFIG_MMC_USHC) += ushc.o
|
||||
|
||||
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
|
||||
sdhci-platform-y := sdhci-pltfm.o
|
||||
sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
|
||||
sdhci-platform-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
|
||||
|
||||
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
|
||||
sdhci-of-y := sdhci-of-core.o
|
||||
|
|
|
@ -928,7 +928,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
|
|||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME))
|
||||
if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
|
||||
|
@ -947,8 +947,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
|
|||
mmc->max_blk_size = MCI_MAXBLKSIZE;
|
||||
mmc->max_blk_count = MCI_BLKATONCE;
|
||||
mmc->max_req_size = MCI_BUFSIZE;
|
||||
mmc->max_phys_segs = MCI_BLKATONCE;
|
||||
mmc->max_hw_segs = MCI_BLKATONCE;
|
||||
mmc->max_segs = MCI_BLKATONCE;
|
||||
mmc->max_seg_size = MCI_BUFSIZE;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
|
@ -1017,7 +1016,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
|
|||
/*
|
||||
* Map I/O region
|
||||
*/
|
||||
host->baseaddr = ioremap(res->start, res->end - res->start + 1);
|
||||
host->baseaddr = ioremap(res->start, resource_size(res));
|
||||
if (!host->baseaddr) {
|
||||
ret = -ENOMEM;
|
||||
goto fail1;
|
||||
|
@ -1093,7 +1092,7 @@ fail4b:
|
|||
fail5:
|
||||
mmc_free_host(mmc);
|
||||
fail6:
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
dev_err(&pdev->dev, "probe failed, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1138,7 +1137,7 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
|
|||
|
||||
iounmap(host->baseaddr);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
mmc_free_host(mmc);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
|
|
@ -1618,8 +1618,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
|
|||
if (slot_data->bus_width >= 4)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
|
||||
mmc->max_hw_segs = 64;
|
||||
mmc->max_phys_segs = 64;
|
||||
mmc->max_segs = 64;
|
||||
mmc->max_req_size = 32768 * 512;
|
||||
mmc->max_blk_size = 32768;
|
||||
mmc->max_blk_count = 512;
|
||||
|
@ -1777,7 +1776,7 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
host->regs = ioremap(regs->start, regs->end - regs->start + 1);
|
||||
host->regs = ioremap(regs->start, resource_size(regs));
|
||||
if (!host->regs)
|
||||
goto err_ioremap;
|
||||
|
||||
|
|
|
@ -964,7 +964,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
|
|||
goto out1;
|
||||
}
|
||||
|
||||
host->ioarea = request_mem_region(r->start, r->end - r->start + 1,
|
||||
host->ioarea = request_mem_region(r->start, resource_size(r),
|
||||
pdev->name);
|
||||
if (!host->ioarea) {
|
||||
dev_err(&pdev->dev, "mmio already in use\n");
|
||||
|
@ -998,7 +998,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
|
|||
mmc->f_max = 24000000;
|
||||
|
||||
mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
|
||||
mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT;
|
||||
mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
|
||||
|
||||
mmc->max_blk_size = 2048;
|
||||
mmc->max_blk_count = 512;
|
||||
|
|
|
@ -469,7 +469,7 @@ static int __devinit sdh_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
mmc->ops = &sdh_ops;
|
||||
mmc->max_phys_segs = 32;
|
||||
mmc->max_segs = 32;
|
||||
mmc->max_seg_size = 1 << 16;
|
||||
mmc->max_blk_size = 1 << 11;
|
||||
mmc->max_blk_count = 1 << 11;
|
||||
|
|
|
@ -25,7 +25,7 @@ static const u8 cb710_src_freq_mhz[16] = {
|
|||
50, 55, 60, 65, 70, 75, 80, 85
|
||||
};
|
||||
|
||||
static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz)
|
||||
static void cb710_mmc_select_clock_divider(struct mmc_host *mmc, int hz)
|
||||
{
|
||||
struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
|
||||
struct pci_dev *pdev = cb710_slot_to_chip(slot)->pdev;
|
||||
|
@ -33,8 +33,11 @@ static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz)
|
|||
u32 divider_idx;
|
||||
int src_hz;
|
||||
|
||||
/* this is magic, unverifiable for me, unless I get
|
||||
* MMC card with cables connected to bus signals */
|
||||
/* on CB710 in HP nx9500:
|
||||
* src_freq_idx == 0
|
||||
* indexes 1-7 work as written in the table
|
||||
* indexes 0,8-15 give no clock output
|
||||
*/
|
||||
pci_read_config_dword(pdev, 0x48, &src_freq_idx);
|
||||
src_freq_idx = (src_freq_idx >> 16) & 0xF;
|
||||
src_hz = cb710_src_freq_mhz[src_freq_idx] * 1000000;
|
||||
|
@ -46,13 +49,15 @@ static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz)
|
|||
|
||||
if (src_freq_idx)
|
||||
divider_idx |= 0x8;
|
||||
else if (divider_idx == 0)
|
||||
divider_idx = 1;
|
||||
|
||||
cb710_pci_update_config_reg(pdev, 0x40, ~0xF0000000, divider_idx << 28);
|
||||
|
||||
dev_dbg(cb710_slot_dev(slot),
|
||||
"clock set to %d Hz, wanted %d Hz; flag = %d\n",
|
||||
"clock set to %d Hz, wanted %d Hz; src_freq_idx = %d, divider_idx = %d|%d\n",
|
||||
src_hz >> cb710_clock_divider_log2[divider_idx & 7],
|
||||
hz, (divider_idx & 8) != 0);
|
||||
hz, src_freq_idx, divider_idx & 7, divider_idx & 8);
|
||||
}
|
||||
|
||||
static void __cb710_mmc_enable_irq(struct cb710_slot *slot,
|
||||
|
@ -95,16 +100,8 @@ static void cb710_mmc_reset_events(struct cb710_slot *slot)
|
|||
cb710_write_port_8(slot, CB710_MMC_STATUS2_PORT, 0xFF);
|
||||
}
|
||||
|
||||
static int cb710_mmc_is_card_inserted(struct cb710_slot *slot)
|
||||
{
|
||||
return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT)
|
||||
& CB710_MMC_S3_CARD_DETECTED;
|
||||
}
|
||||
|
||||
static void cb710_mmc_enable_4bit_data(struct cb710_slot *slot, int enable)
|
||||
{
|
||||
dev_dbg(cb710_slot_dev(slot), "configuring %d-data-line%s mode\n",
|
||||
enable ? 4 : 1, enable ? "s" : "");
|
||||
if (enable)
|
||||
cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT,
|
||||
CB710_MMC_C1_4BIT_DATA_BUS, 0);
|
||||
|
@ -494,13 +491,8 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
reader->mrq = mrq;
|
||||
cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0);
|
||||
|
||||
if (cb710_mmc_is_card_inserted(slot)) {
|
||||
if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
|
||||
cb710_mmc_command(mmc, mrq->stop);
|
||||
mdelay(1);
|
||||
} else {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
}
|
||||
if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop)
|
||||
cb710_mmc_command(mmc, mrq->stop);
|
||||
|
||||
tasklet_schedule(&reader->finish_req_tasklet);
|
||||
}
|
||||
|
@ -512,7 +504,7 @@ static int cb710_mmc_powerup(struct cb710_slot *slot)
|
|||
#endif
|
||||
int err;
|
||||
|
||||
/* a lot of magic; see comment in cb710_mmc_set_clock() */
|
||||
/* a lot of magic for now */
|
||||
dev_dbg(cb710_slot_dev(slot), "bus powerup\n");
|
||||
cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
|
||||
err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20);
|
||||
|
@ -572,13 +564,7 @@ static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
struct cb710_mmc_reader *reader = mmc_priv(mmc);
|
||||
int err;
|
||||
|
||||
cb710_mmc_set_clock(mmc, ios->clock);
|
||||
|
||||
if (!cb710_mmc_is_card_inserted(slot)) {
|
||||
dev_dbg(cb710_slot_dev(slot),
|
||||
"no card inserted - ignoring bus powerup request\n");
|
||||
ios->power_mode = MMC_POWER_OFF;
|
||||
}
|
||||
cb710_mmc_select_clock_divider(mmc, ios->clock);
|
||||
|
||||
if (ios->power_mode != reader->last_power_mode)
|
||||
switch (ios->power_mode) {
|
||||
|
@ -619,6 +605,14 @@ static int cb710_mmc_get_ro(struct mmc_host *mmc)
|
|||
& CB710_MMC_S3_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
static int cb710_mmc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct cb710_slot *slot = cb710_mmc_to_slot(mmc);
|
||||
|
||||
return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT)
|
||||
& CB710_MMC_S3_CARD_DETECTED;
|
||||
}
|
||||
|
||||
static int cb710_mmc_irq_handler(struct cb710_slot *slot)
|
||||
{
|
||||
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
|
||||
|
@ -664,7 +658,8 @@ static void cb710_mmc_finish_request_tasklet(unsigned long data)
|
|||
static const struct mmc_host_ops cb710_mmc_host = {
|
||||
.request = cb710_mmc_request,
|
||||
.set_ios = cb710_mmc_set_ios,
|
||||
.get_ro = cb710_mmc_get_ro
|
||||
.get_ro = cb710_mmc_get_ro,
|
||||
.get_cd = cb710_mmc_get_cd,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -746,6 +741,7 @@ static int __devinit cb710_mmc_init(struct platform_device *pdev)
|
|||
err_free_mmc:
|
||||
dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err);
|
||||
|
||||
cb710_set_irq_handler(slot, NULL);
|
||||
mmc_free_host(mmc);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
/*
|
||||
* One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
|
||||
* and we handle up to MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
|
||||
* for drivers with max_hw_segs == 1, making the segments bigger (64KB)
|
||||
* for drivers with max_segs == 1, making the segments bigger (64KB)
|
||||
* than the page or two that's otherwise typical. nr_sg (passed from
|
||||
* platform data) == 16 gives at least the same throughput boost, using
|
||||
* EDMA transfer linkage instead of spending CPU time copying pages.
|
||||
|
@ -1239,8 +1239,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
|
|||
* Each hw_seg uses one EDMA parameter RAM slot, always one
|
||||
* channel and then usually some linked slots.
|
||||
*/
|
||||
mmc->max_hw_segs = 1 + host->n_link;
|
||||
mmc->max_phys_segs = mmc->max_hw_segs;
|
||||
mmc->max_segs = 1 + host->n_link;
|
||||
|
||||
/* EDMA limit per hw segment (one or two MBytes) */
|
||||
mmc->max_seg_size = MAX_CCNT * rw_threshold;
|
||||
|
@ -1250,8 +1249,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
|
|||
mmc->max_blk_count = 65535; /* NBLK is 16 bits */
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs);
|
||||
dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs);
|
||||
dev_dbg(mmc_dev(host->mmc), "max_segs=%d\n", mmc->max_segs);
|
||||
dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size);
|
||||
dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size);
|
||||
dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size);
|
||||
|
|
|
@ -966,8 +966,7 @@ static int __init imxmci_probe(struct platform_device *pdev)
|
|||
mmc->caps = MMC_CAP_4_BIT_DATA;
|
||||
|
||||
/* MMC core transfer sizes tunable parameters */
|
||||
mmc->max_hw_segs = 64;
|
||||
mmc->max_phys_segs = 64;
|
||||
mmc->max_segs = 64;
|
||||
mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */
|
||||
mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */
|
||||
mmc->max_blk_size = 2048;
|
||||
|
|
|
@ -876,8 +876,7 @@ static int __devinit jz4740_mmc_probe(struct platform_device* pdev)
|
|||
mmc->max_blk_count = (1 << 15) - 1;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
||||
mmc->max_phys_segs = 128;
|
||||
mmc->max_hw_segs = 128;
|
||||
mmc->max_segs = 128;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
host->mmc = mmc;
|
||||
|
|
|
@ -1055,6 +1055,8 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
{
|
||||
struct mmc_spi_host *host = mmc_priv(mmc);
|
||||
int status = -EINVAL;
|
||||
int crc_retry = 5;
|
||||
struct mmc_command stop;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* MMC core and layered drivers *MUST* issue SPI-aware commands */
|
||||
|
@ -1087,10 +1089,29 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
/* request exclusive bus access */
|
||||
spi_bus_lock(host->spi->master);
|
||||
|
||||
crc_recover:
|
||||
/* issue command; then optionally data and stop */
|
||||
status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
|
||||
if (status == 0 && mrq->data) {
|
||||
mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
|
||||
|
||||
/*
|
||||
* The SPI bus is not always reliable for large data transfers.
|
||||
* If an occasional crc error is reported by the SD device with
|
||||
* data read/write over SPI, it may be recovered by repeating
|
||||
* the last SD command again. The retry count is set to 5 to
|
||||
* ensure the driver passes stress tests.
|
||||
*/
|
||||
if (mrq->data->error == -EILSEQ && crc_retry) {
|
||||
stop.opcode = MMC_STOP_TRANSMISSION;
|
||||
stop.arg = 0;
|
||||
stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
status = mmc_spi_command_send(host, mrq, &stop, 0);
|
||||
crc_retry--;
|
||||
mrq->data->error = 0;
|
||||
goto crc_recover;
|
||||
}
|
||||
|
||||
if (mrq->stop)
|
||||
status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
|
||||
else
|
||||
|
@ -1345,8 +1366,7 @@ static int mmc_spi_probe(struct spi_device *spi)
|
|||
|
||||
mmc->ops = &mmc_spi_ops;
|
||||
mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
|
||||
mmc->max_hw_segs = MMC_SPI_BLOCKSATONCE;
|
||||
mmc->max_phys_segs = MMC_SPI_BLOCKSATONCE;
|
||||
mmc->max_segs = MMC_SPI_BLOCKSATONCE;
|
||||
mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE;
|
||||
mmc->max_blk_count = MMC_SPI_BLOCKSATONCE;
|
||||
|
||||
|
|
|
@ -523,19 +523,27 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
struct mmci_host *host = mmc_priv(mmc);
|
||||
u32 pwr = 0;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
if(host->vcc &&
|
||||
regulator_is_enabled(host->vcc))
|
||||
regulator_disable(host->vcc);
|
||||
if (host->vcc)
|
||||
ret = mmc_regulator_set_ocr(mmc, host->vcc, 0);
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
#ifdef CONFIG_REGULATOR
|
||||
if (host->vcc)
|
||||
/* This implicitly enables the regulator */
|
||||
mmc_regulator_set_ocr(host->vcc, ios->vdd);
|
||||
#endif
|
||||
if (host->vcc) {
|
||||
ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(mmc), "unable to set OCR\n");
|
||||
/*
|
||||
* The .set_ios() function in the mmc_host_ops
|
||||
* struct return void, and failing to set the
|
||||
* power should be rare so we print an error
|
||||
* and return here.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (host->plat->vdd_handler)
|
||||
pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd,
|
||||
ios->power_mode);
|
||||
|
@ -734,8 +742,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
|
|||
/*
|
||||
* We can do SGIO
|
||||
*/
|
||||
mmc->max_hw_segs = 16;
|
||||
mmc->max_phys_segs = NR_SG;
|
||||
mmc->max_segs = NR_SG;
|
||||
|
||||
/*
|
||||
* Since only a certain number of bits are valid in the data length
|
||||
|
@ -870,8 +877,8 @@ static int __devexit mmci_remove(struct amba_device *dev)
|
|||
clk_disable(host->clk);
|
||||
clk_put(host->clk);
|
||||
|
||||
if (regulator_is_enabled(host->vcc))
|
||||
regulator_disable(host->vcc);
|
||||
if (host->vcc)
|
||||
mmc_regulator_set_ocr(mmc, host->vcc, 0);
|
||||
regulator_put(host->vcc);
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
|
|
@ -1164,8 +1164,7 @@ msmsdcc_probe(struct platform_device *pdev)
|
|||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
|
||||
|
||||
mmc->max_phys_segs = NR_SG;
|
||||
mmc->max_hw_segs = NR_SG;
|
||||
mmc->max_segs = NR_SG;
|
||||
mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */
|
||||
mmc->max_blk_count = 65536;
|
||||
|
||||
|
|
|
@ -742,8 +742,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
|||
mmc->max_blk_size = 2048;
|
||||
mmc->max_blk_count = 65535;
|
||||
|
||||
mmc->max_hw_segs = 1;
|
||||
mmc->max_phys_segs = 1;
|
||||
mmc->max_segs = 1;
|
||||
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
||||
|
|
|
@ -790,8 +790,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
||||
|
||||
/* MMC core transfer sizes tunable parameters */
|
||||
mmc->max_hw_segs = 64;
|
||||
mmc->max_phys_segs = 64;
|
||||
mmc->max_segs = 64;
|
||||
mmc->max_blk_size = 2048;
|
||||
mmc->max_blk_count = 65535;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
|
|
@ -1335,8 +1335,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
|
|||
* NOTE max_seg_size assumption that small blocks aren't
|
||||
* normally used (except e.g. for reading SD registers).
|
||||
*/
|
||||
mmc->max_phys_segs = 32;
|
||||
mmc->max_hw_segs = 32;
|
||||
mmc->max_segs = 32;
|
||||
mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */
|
||||
mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
|
|
@ -250,9 +250,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on,
|
|||
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
|
||||
|
||||
if (power_on)
|
||||
ret = mmc_regulator_set_ocr(host->vcc, vdd);
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
else
|
||||
ret = mmc_regulator_set_ocr(host->vcc, 0);
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
|
||||
|
||||
if (mmc_slot(host).after_set_reg)
|
||||
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
|
||||
|
@ -291,18 +291,23 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,
|
|||
* chips/cards need an interface voltage rail too.
|
||||
*/
|
||||
if (power_on) {
|
||||
ret = mmc_regulator_set_ocr(host->vcc, vdd);
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
/* Enable interface voltage rail, if needed */
|
||||
if (ret == 0 && host->vcc_aux) {
|
||||
ret = regulator_enable(host->vcc_aux);
|
||||
if (ret < 0)
|
||||
ret = mmc_regulator_set_ocr(host->vcc, 0);
|
||||
ret = mmc_regulator_set_ocr(host->mmc,
|
||||
host->vcc, 0);
|
||||
}
|
||||
} else {
|
||||
/* Shut down the rail */
|
||||
if (host->vcc_aux)
|
||||
ret = regulator_disable(host->vcc_aux);
|
||||
if (ret == 0)
|
||||
ret = mmc_regulator_set_ocr(host->vcc, 0);
|
||||
if (!ret) {
|
||||
/* Then proceed to shut down the local regulator */
|
||||
ret = mmc_regulator_set_ocr(host->mmc,
|
||||
host->vcc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mmc_slot(host).after_set_reg)
|
||||
|
@ -343,9 +348,9 @@ static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep,
|
|||
if (cardsleep) {
|
||||
/* VCC can be turned off if card is asleep */
|
||||
if (sleep)
|
||||
err = mmc_regulator_set_ocr(host->vcc, 0);
|
||||
err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
|
||||
else
|
||||
err = mmc_regulator_set_ocr(host->vcc, vdd);
|
||||
err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
} else
|
||||
err = regulator_set_mode(host->vcc, mode);
|
||||
if (err)
|
||||
|
@ -2130,8 +2135,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
/* Since we do only SG emulation, we can have as many segs
|
||||
* as we want. */
|
||||
mmc->max_phys_segs = 1024;
|
||||
mmc->max_hw_segs = 1024;
|
||||
mmc->max_segs = 1024;
|
||||
|
||||
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
|
||||
mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
|
||||
|
|
|
@ -99,14 +99,25 @@ static inline void pxamci_init_ocr(struct pxamci_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
|
||||
static inline int pxamci_set_power(struct pxamci_host *host,
|
||||
unsigned char power_mode,
|
||||
unsigned int vdd)
|
||||
{
|
||||
int on;
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
if (host->vcc)
|
||||
mmc_regulator_set_ocr(host->vcc, vdd);
|
||||
#endif
|
||||
if (host->vcc) {
|
||||
int ret;
|
||||
|
||||
if (power_mode == MMC_POWER_UP) {
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (power_mode == MMC_POWER_OFF) {
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (!host->vcc && host->pdata &&
|
||||
gpio_is_valid(host->pdata->gpio_power)) {
|
||||
on = ((1 << vdd) & host->pdata->ocr_mask);
|
||||
|
@ -115,6 +126,8 @@ static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
|
|||
}
|
||||
if (!host->vcc && host->pdata && host->pdata->setpower)
|
||||
host->pdata->setpower(mmc_dev(host->mmc), vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxamci_stop_clock(struct pxamci_host *host)
|
||||
|
@ -490,9 +503,21 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
}
|
||||
|
||||
if (host->power_mode != ios->power_mode) {
|
||||
int ret;
|
||||
|
||||
host->power_mode = ios->power_mode;
|
||||
|
||||
pxamci_set_power(host, ios->vdd);
|
||||
ret = pxamci_set_power(host, ios->power_mode, ios->vdd);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(mmc), "unable to set power\n");
|
||||
/*
|
||||
* The .set_ios() function in the mmc_host_ops
|
||||
* struct return void, and failing to set the
|
||||
* power should be rare so we print an error and
|
||||
* return here.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (ios->power_mode == MMC_POWER_ON)
|
||||
host->cmdat |= CMDAT_INIT;
|
||||
|
@ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
else
|
||||
host->cmdat &= ~CMDAT_SD_4DAT;
|
||||
|
||||
pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
|
||||
host->clkrt, host->cmdat);
|
||||
dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n",
|
||||
host->clkrt, host->cmdat);
|
||||
}
|
||||
|
||||
static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
|
||||
|
@ -576,7 +601,7 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
* We can do SG-DMA, but we don't because we never know how much
|
||||
* data we successfully wrote to the card.
|
||||
*/
|
||||
mmc->max_phys_segs = NR_SG;
|
||||
mmc->max_segs = NR_SG;
|
||||
|
||||
/*
|
||||
* Our hardware DMA can handle a maximum of one page per SG entry.
|
||||
|
|
|
@ -1736,8 +1736,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
|
|||
mmc->max_req_size = 4095 * 512;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
mmc->max_phys_segs = 128;
|
||||
mmc->max_hw_segs = 128;
|
||||
mmc->max_segs = 128;
|
||||
|
||||
dbg(host, dbg_debug,
|
||||
"probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/sdhci-pltfm.h>
|
||||
#include <linux/mmc/sdhci-pltfm.h>
|
||||
#include <mach/cns3xxx.h>
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Freescale eSDHC i.MX controller driver for the platform bus.
|
||||
*
|
||||
* derived from the OF-version.
|
||||
*
|
||||
* Copyright (c) 2010 Pengutronix e.K.
|
||||
* Author: Wolfram Sang <w.sang@pengutronix.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sdhci-pltfm.h>
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "sdhci-esdhc.h"
|
||||
|
||||
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
|
||||
{
|
||||
void __iomem *base = host->ioaddr + (reg & ~0x3);
|
||||
u32 shift = (reg & 0x3) * 8;
|
||||
|
||||
writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
|
||||
}
|
||||
|
||||
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
||||
{
|
||||
if (unlikely(reg == SDHCI_HOST_VERSION))
|
||||
reg ^= 2;
|
||||
|
||||
return readw(host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
switch (reg) {
|
||||
case SDHCI_TRANSFER_MODE:
|
||||
/*
|
||||
* Postpone this write, we must do it together with a
|
||||
* command write that is down below.
|
||||
*/
|
||||
pltfm_host->scratchpad = val;
|
||||
return;
|
||||
case SDHCI_COMMAND:
|
||||
writel(val << 16 | pltfm_host->scratchpad,
|
||||
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||
return;
|
||||
case SDHCI_BLOCK_SIZE:
|
||||
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
|
||||
break;
|
||||
}
|
||||
esdhc_clrset_le(host, 0xffff, val, reg);
|
||||
}
|
||||
|
||||
static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
u32 new_val;
|
||||
|
||||
switch (reg) {
|
||||
case SDHCI_POWER_CONTROL:
|
||||
/*
|
||||
* FSL put some DMA bits here
|
||||
* If your board has a regulator, code should be here
|
||||
*/
|
||||
return;
|
||||
case SDHCI_HOST_CONTROL:
|
||||
/* FSL messed up here, so we can just keep those two */
|
||||
new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS);
|
||||
/* ensure the endianess */
|
||||
new_val |= ESDHC_HOST_CONTROL_LE;
|
||||
/* DMA mode bits are shifted */
|
||||
new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
|
||||
|
||||
esdhc_clrset_le(host, 0xffff, new_val, reg);
|
||||
return;
|
||||
}
|
||||
esdhc_clrset_le(host, 0xff, val, reg);
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_get_rate(pltfm_host->clk);
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_get_rate(pltfm_host->clk) / 256 / 16;
|
||||
}
|
||||
|
||||
static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct clk *clk;
|
||||
|
||||
clk = clk_get(mmc_dev(host->mmc), NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(mmc_dev(host->mmc), "clk err\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
clk_enable(clk);
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esdhc_pltfm_exit(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
clk_disable(pltfm_host->clk);
|
||||
clk_put(pltfm_host->clk);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_w = esdhc_readw_le,
|
||||
.write_w = esdhc_writew_le,
|
||||
.write_b = esdhc_writeb_le,
|
||||
.set_clock = esdhc_set_clock,
|
||||
.get_max_clock = esdhc_pltfm_get_max_clock,
|
||||
.get_min_clock = esdhc_pltfm_get_min_clock,
|
||||
};
|
||||
|
||||
struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_MULTIBLOCK
|
||||
| SDHCI_QUIRK_BROKEN_ADMA,
|
||||
/* ADMA has issues. Might be fixable */
|
||||
/* NO_MULTIBLOCK might be MX35 only (Errata: ENGcm07207) */
|
||||
.ops = &sdhci_esdhc_ops,
|
||||
.init = esdhc_pltfm_init,
|
||||
.exit = esdhc_pltfm_exit,
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Freescale eSDHC controller driver generics for OF and pltfm.
|
||||
*
|
||||
* Copyright (c) 2007 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Copyright (c) 2010 Pengutronix e.K.
|
||||
* Author: Wolfram Sang <w.sang@pengutronix.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS_MMC_SDHCI_ESDHC_H
|
||||
#define _DRIVERS_MMC_SDHCI_ESDHC_H
|
||||
|
||||
/*
|
||||
* Ops and quirks for the Freescale eSDHC controller.
|
||||
*/
|
||||
|
||||
#define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \
|
||||
SDHCI_QUIRK_BROKEN_CARD_DETECTION | \
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ | \
|
||||
SDHCI_QUIRK_NONSTANDARD_CLOCK | \
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
|
||||
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET | \
|
||||
SDHCI_QUIRK_NO_CARD_NO_RESET)
|
||||
|
||||
#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
#define ESDHC_PREDIV_SHIFT 8
|
||||
#define ESDHC_DIVIDER_SHIFT 4
|
||||
#define ESDHC_CLOCK_PEREN 0x00000004
|
||||
#define ESDHC_CLOCK_HCKEN 0x00000002
|
||||
#define ESDHC_CLOCK_IPGEN 0x00000001
|
||||
|
||||
/* pltfm-specific */
|
||||
#define ESDHC_HOST_CONTROL_LE 0x20
|
||||
|
||||
/* OF-specific */
|
||||
#define ESDHC_DMA_SYSCTL 0x40c
|
||||
#define ESDHC_DMA_SNOOP 0x00000040
|
||||
|
||||
#define ESDHC_HOST_CONTROL_RES 0x05
|
||||
|
||||
static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
int pre_div = 2;
|
||||
int div = 1;
|
||||
u32 temp;
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
|
||||
| ESDHC_CLOCK_MASK);
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
if (clock == 0)
|
||||
goto out;
|
||||
|
||||
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
|
||||
pre_div *= 2;
|
||||
|
||||
while (host->max_clk / pre_div / div > clock && div < 16)
|
||||
div++;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
||||
clock, host->max_clk / pre_div / div);
|
||||
|
||||
pre_div >>= 1;
|
||||
div--;
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
|
||||
| (div << ESDHC_DIVIDER_SHIFT)
|
||||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
mdelay(100);
|
||||
out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
|
|
@ -18,23 +18,7 @@
|
|||
#include <linux/mmc/host.h>
|
||||
#include "sdhci-of.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
/*
|
||||
* Ops and quirks for the Freescale eSDHC controller.
|
||||
*/
|
||||
|
||||
#define ESDHC_DMA_SYSCTL 0x40c
|
||||
#define ESDHC_DMA_SNOOP 0x00000040
|
||||
|
||||
#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
#define ESDHC_PREDIV_SHIFT 8
|
||||
#define ESDHC_DIVIDER_SHIFT 4
|
||||
#define ESDHC_CLOCK_PEREN 0x00000004
|
||||
#define ESDHC_CLOCK_HCKEN 0x00000002
|
||||
#define ESDHC_CLOCK_IPGEN 0x00000001
|
||||
|
||||
#define ESDHC_HOST_CONTROL_RES 0x05
|
||||
#include "sdhci-esdhc.h"
|
||||
|
||||
static u16 esdhc_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
|
@ -68,51 +52,20 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
|
|||
sdhci_be32bs_writeb(host, val, reg);
|
||||
}
|
||||
|
||||
static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
int pre_div = 2;
|
||||
int div = 1;
|
||||
|
||||
clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
|
||||
ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
|
||||
|
||||
if (clock == 0)
|
||||
goto out;
|
||||
|
||||
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
|
||||
pre_div *= 2;
|
||||
|
||||
while (host->max_clk / pre_div / div > clock && div < 16)
|
||||
div++;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
||||
clock, host->max_clk / pre_div / div);
|
||||
|
||||
pre_div >>= 1;
|
||||
div--;
|
||||
|
||||
setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
|
||||
ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
|
||||
div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
|
||||
mdelay(100);
|
||||
out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
static int esdhc_enable_dma(struct sdhci_host *host)
|
||||
static int esdhc_of_enable_dma(struct sdhci_host *host)
|
||||
{
|
||||
setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
|
||||
static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_of_host *of_host = sdhci_priv(host);
|
||||
|
||||
return of_host->clock;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_get_min_clock(struct sdhci_host *host)
|
||||
static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_of_host *of_host = sdhci_priv(host);
|
||||
|
||||
|
@ -120,14 +73,7 @@ static unsigned int esdhc_get_min_clock(struct sdhci_host *host)
|
|||
}
|
||||
|
||||
struct sdhci_of_data sdhci_esdhc = {
|
||||
.quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
|
||||
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ |
|
||||
SDHCI_QUIRK_NONSTANDARD_CLOCK |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY |
|
||||
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
|
||||
SDHCI_QUIRK_NO_CARD_NO_RESET,
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS,
|
||||
.ops = {
|
||||
.read_l = sdhci_be32bs_readl,
|
||||
.read_w = esdhc_readw,
|
||||
|
@ -136,8 +82,8 @@ struct sdhci_of_data sdhci_esdhc = {
|
|||
.write_w = esdhc_writew,
|
||||
.write_b = esdhc_writeb,
|
||||
.set_clock = esdhc_set_clock,
|
||||
.enable_dma = esdhc_enable_dma,
|
||||
.get_max_clock = esdhc_get_max_clock,
|
||||
.get_min_clock = esdhc_get_min_clock,
|
||||
.enable_dma = esdhc_of_enable_dma,
|
||||
.get_max_clock = esdhc_of_get_max_clock,
|
||||
.get_min_clock = esdhc_of_get_min_clock,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -145,6 +145,37 @@ static const struct sdhci_pci_fixes sdhci_cafe = {
|
|||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
};
|
||||
|
||||
/*
|
||||
* ADMA operation is disabled for Moorestown platform due to
|
||||
* hardware bugs.
|
||||
*/
|
||||
static int mrst_hc1_probe(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
/*
|
||||
* slots number is fixed here for MRST as SDIO3 is never used and has
|
||||
* hardware bugs.
|
||||
*/
|
||||
chip->num_slots = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1 = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
.probe = mrst_hc1_probe,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
};
|
||||
|
||||
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
|
||||
{
|
||||
u8 scratch;
|
||||
|
@ -494,6 +525,62 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
|
|||
.driver_data = (kernel_ulong_t)&sdhci_via,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc0,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MRST_SD1,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MFD_SD,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MFD_SDIO1,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MFD_SDIO2,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MFD_EMMC0,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_MFD_EMMC1,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio,
|
||||
},
|
||||
|
||||
{ /* Generic SD host controller */
|
||||
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
|
||||
},
|
||||
|
@ -818,6 +905,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
|||
goto free;
|
||||
}
|
||||
|
||||
slots = chip->num_slots; /* Quirk may have changed this */
|
||||
|
||||
for (i = 0;i < slots;i++) {
|
||||
slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
|
||||
if (IS_ERR(slot)) {
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <linux/mmc/host.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/sdhci-pltfm.h>
|
||||
#include <linux/mmc/sdhci-pltfm.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
@ -52,14 +52,17 @@ static struct sdhci_ops sdhci_pltfm_ops = {
|
|||
|
||||
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
struct sdhci_pltfm_data *pdata;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct resource *iomem;
|
||||
int ret;
|
||||
|
||||
if (!pdata && platid && platid->driver_data)
|
||||
if (platid && platid->driver_data)
|
||||
pdata = (void *)platid->driver_data;
|
||||
else
|
||||
pdata = pdev->dev.platform_data;
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iomem) {
|
||||
|
@ -71,16 +74,19 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "Invalid iomem size. You may "
|
||||
"experience problems.\n");
|
||||
|
||||
if (pdev->dev.parent)
|
||||
host = sdhci_alloc_host(pdev->dev.parent, 0);
|
||||
/* Some PCI-based MFD need the parent here */
|
||||
if (pdev->dev.parent != &platform_bus)
|
||||
host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
|
||||
else
|
||||
host = sdhci_alloc_host(&pdev->dev, 0);
|
||||
host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
|
||||
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
host->hw_name = "platform";
|
||||
if (pdata && pdata->ops)
|
||||
host->ops = pdata->ops;
|
||||
|
@ -105,7 +111,7 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (pdata && pdata->init) {
|
||||
ret = pdata->init(host);
|
||||
ret = pdata->init(host, pdata);
|
||||
if (ret)
|
||||
goto err_plat_init;
|
||||
}
|
||||
|
@ -160,11 +166,33 @@ static const struct platform_device_id sdhci_pltfm_ids[] = {
|
|||
{ "sdhci", },
|
||||
#ifdef CONFIG_MMC_SDHCI_CNS3XXX
|
||||
{ "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata },
|
||||
#endif
|
||||
#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX
|
||||
{ "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata },
|
||||
#endif
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(dev);
|
||||
|
||||
return sdhci_suspend_host(host, state);
|
||||
}
|
||||
|
||||
static int sdhci_pltfm_resume(struct platform_device *dev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(dev);
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#else
|
||||
#define sdhci_pltfm_suspend NULL
|
||||
#define sdhci_pltfm_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct platform_driver sdhci_pltfm_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci",
|
||||
|
@ -173,6 +201,8 @@ static struct platform_driver sdhci_pltfm_driver = {
|
|||
.probe = sdhci_pltfm_probe,
|
||||
.remove = __devexit_p(sdhci_pltfm_remove),
|
||||
.id_table = sdhci_pltfm_ids,
|
||||
.suspend = sdhci_pltfm_suspend,
|
||||
.resume = sdhci_pltfm_resume,
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
|
|
|
@ -11,8 +11,16 @@
|
|||
#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
|
||||
#define _DRIVERS_MMC_SDHCI_PLTFM_H
|
||||
|
||||
#include <linux/sdhci-pltfm.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mmc/sdhci-pltfm.h>
|
||||
|
||||
struct sdhci_pltfm_host {
|
||||
struct clk *clk;
|
||||
u32 scratchpad; /* to handle quirks across io-accessor calls */
|
||||
};
|
||||
|
||||
extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata;
|
||||
extern struct sdhci_pltfm_data sdhci_esdhc_imx_pdata;
|
||||
|
||||
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
/* linux/drivers/mmc/host/sdhci-pxa.c
|
||||
*
|
||||
* Copyright (C) 2010 Marvell International Ltd.
|
||||
* Zhangfei Gao <zhangfei.gao@marvell.com>
|
||||
* Kevin Wang <dwang4@marvell.com>
|
||||
* Mingwei Wang <mwwang@marvell.com>
|
||||
* Philip Rakity <prakity@marvell.com>
|
||||
* Mark Brown <markb@marvell.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
* SDHCI support for MMP2/PXA910/PXA168
|
||||
*
|
||||
* Refer to sdhci-s3c.c.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <plat/sdhci.h>
|
||||
#include "sdhci.h"
|
||||
|
||||
#define DRIVER_NAME "sdhci-pxa"
|
||||
|
||||
#define SD_FIFO_PARAM 0x104
|
||||
#define DIS_PAD_SD_CLK_GATE 0x400
|
||||
|
||||
struct sdhci_pxa {
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pxa_platdata *pdata;
|
||||
struct clk *clk;
|
||||
struct resource *res;
|
||||
|
||||
u8 clk_enable;
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* SDHCI core callbacks *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
static void set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pxa *pxa = sdhci_priv(host);
|
||||
u32 tmp = 0;
|
||||
|
||||
if (clock == 0) {
|
||||
if (pxa->clk_enable) {
|
||||
clk_disable(pxa->clk);
|
||||
pxa->clk_enable = 0;
|
||||
}
|
||||
} else {
|
||||
if (0 == pxa->clk_enable) {
|
||||
if (pxa->pdata->flags & PXA_FLAG_DISABLE_CLOCK_GATING) {
|
||||
tmp = readl(host->ioaddr + SD_FIFO_PARAM);
|
||||
tmp |= DIS_PAD_SD_CLK_GATE;
|
||||
writel(tmp, host->ioaddr + SD_FIFO_PARAM);
|
||||
}
|
||||
clk_enable(pxa->clk);
|
||||
pxa->clk_enable = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_pxa_ops = {
|
||||
.set_clock = set_clock,
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Device probing/removal *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sdhci_host *host = NULL;
|
||||
struct resource *iomem = NULL;
|
||||
struct sdhci_pxa *pxa = NULL;
|
||||
int ret, irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq specified\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iomem) {
|
||||
dev_err(dev, "no memory specified\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pxa));
|
||||
if (IS_ERR(host)) {
|
||||
dev_err(dev, "failed to alloc host\n");
|
||||
return PTR_ERR(host);
|
||||
}
|
||||
|
||||
pxa = sdhci_priv(host);
|
||||
pxa->host = host;
|
||||
pxa->pdata = pdata;
|
||||
pxa->clk_enable = 0;
|
||||
|
||||
pxa->clk = clk_get(dev, "PXA-SDHCLK");
|
||||
if (IS_ERR(pxa->clk)) {
|
||||
dev_err(dev, "failed to get io clock\n");
|
||||
ret = PTR_ERR(pxa->clk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pxa->res = request_mem_region(iomem->start, resource_size(iomem),
|
||||
mmc_hostname(host->mmc));
|
||||
if (!pxa->res) {
|
||||
dev_err(&pdev->dev, "cannot request region\n");
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
host->ioaddr = ioremap(iomem->start, resource_size(iomem));
|
||||
if (!host->ioaddr) {
|
||||
dev_err(&pdev->dev, "failed to remap registers\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
host->hw_name = "MMC";
|
||||
host->ops = &sdhci_pxa_ops;
|
||||
host->irq = irq;
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
if (pdata->quirks)
|
||||
host->quirks |= pdata->quirks;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add host\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pxa->pdata->max_speed)
|
||||
host->mmc->f_max = pxa->pdata->max_speed;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (host) {
|
||||
clk_put(pxa->clk);
|
||||
if (host->ioaddr)
|
||||
iounmap(host->ioaddr);
|
||||
if (pxa->res)
|
||||
release_mem_region(pxa->res->start,
|
||||
resource_size(pxa->res));
|
||||
sdhci_free_host(host);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit sdhci_pxa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pxa *pxa = sdhci_priv(host);
|
||||
int dead = 0;
|
||||
u32 scratch;
|
||||
|
||||
if (host) {
|
||||
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
|
||||
if (scratch == (u32)-1)
|
||||
dead = 1;
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
if (host->ioaddr)
|
||||
iounmap(host->ioaddr);
|
||||
if (pxa->res)
|
||||
release_mem_region(pxa->res->start,
|
||||
resource_size(pxa->res));
|
||||
if (pxa->clk_enable) {
|
||||
clk_disable(pxa->clk);
|
||||
pxa->clk_enable = 0;
|
||||
}
|
||||
clk_put(pxa->clk);
|
||||
|
||||
sdhci_free_host(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_pxa_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(dev);
|
||||
|
||||
return sdhci_suspend_host(host, state);
|
||||
}
|
||||
|
||||
static int sdhci_pxa_resume(struct platform_device *dev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(dev);
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#else
|
||||
#define sdhci_pxa_suspend NULL
|
||||
#define sdhci_pxa_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver sdhci_pxa_driver = {
|
||||
.probe = sdhci_pxa_probe,
|
||||
.remove = __devexit_p(sdhci_pxa_remove),
|
||||
.suspend = sdhci_pxa_suspend,
|
||||
.resume = sdhci_pxa_resume,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Driver init/exit *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static int __init sdhci_pxa_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_pxa_driver);
|
||||
}
|
||||
|
||||
static void __exit sdhci_pxa_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_pxa_driver);
|
||||
}
|
||||
|
||||
module_init(sdhci_pxa_init);
|
||||
module_exit(sdhci_pxa_exit);
|
||||
|
||||
MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2");
|
||||
MODULE_AUTHOR("Zhangfei Gao <zhangfei.gao@marvell.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -47,7 +47,8 @@ static void sdhci_finish_command(struct sdhci_host *);
|
|||
|
||||
static void sdhci_dumpregs(struct sdhci_host *host)
|
||||
{
|
||||
printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n");
|
||||
printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
|
||||
mmc_hostname(host->mmc));
|
||||
|
||||
printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
|
||||
sdhci_readl(host, SDHCI_DMA_ADDRESS),
|
||||
|
@ -1001,13 +1002,28 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
if (clock == 0)
|
||||
goto out;
|
||||
|
||||
for (div = 1;div < 256;div *= 2) {
|
||||
if ((host->max_clk / div) <= clock)
|
||||
break;
|
||||
if (host->version >= SDHCI_SPEC_300) {
|
||||
/* Version 3.00 divisors must be a multiple of 2. */
|
||||
if (host->max_clk <= clock)
|
||||
div = 1;
|
||||
else {
|
||||
for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
|
||||
if ((host->max_clk / div) <= clock)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Version 2.00 divisors must be a power of 2. */
|
||||
for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
|
||||
if ((host->max_clk / div) <= clock)
|
||||
break;
|
||||
}
|
||||
}
|
||||
div >>= 1;
|
||||
|
||||
clk = div << SDHCI_DIVIDER_SHIFT;
|
||||
clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
|
||||
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
|
||||
<< SDHCI_DIVIDER_HI_SHIFT;
|
||||
clk |= SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
|
@ -1034,11 +1050,9 @@ out:
|
|||
|
||||
static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
||||
{
|
||||
u8 pwr;
|
||||
u8 pwr = 0;
|
||||
|
||||
if (power == (unsigned short)-1)
|
||||
pwr = 0;
|
||||
else {
|
||||
if (power != (unsigned short)-1) {
|
||||
switch (1 << power) {
|
||||
case MMC_VDD_165_195:
|
||||
pwr = SDHCI_POWER_180;
|
||||
|
@ -1168,6 +1182,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
else
|
||||
sdhci_set_power(host, ios->vdd);
|
||||
|
||||
if (host->ops->platform_send_init_74_clocks)
|
||||
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
|
@ -1180,8 +1197,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
else
|
||||
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
|
||||
if (ios->timing == MMC_TIMING_SD_HS &&
|
||||
!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
|
||||
if ((ios->timing == MMC_TIMING_SD_HS ||
|
||||
ios->timing == MMC_TIMING_MMC_HS)
|
||||
&& !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
|
||||
ctrl |= SDHCI_CTRL_HISPD;
|
||||
else
|
||||
ctrl &= ~SDHCI_CTRL_HISPD;
|
||||
|
@ -1205,22 +1223,25 @@ static int sdhci_get_ro(struct mmc_host *mmc)
|
|||
{
|
||||
struct sdhci_host *host;
|
||||
unsigned long flags;
|
||||
int present;
|
||||
int is_readonly;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||
present = 0;
|
||||
is_readonly = 0;
|
||||
else if (host->ops->get_ro)
|
||||
is_readonly = host->ops->get_ro(host);
|
||||
else
|
||||
present = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||
is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
|
||||
& SDHCI_WRITE_PROTECT);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
|
||||
return !!(present & SDHCI_WRITE_PROTECT);
|
||||
return !(present & SDHCI_WRITE_PROTECT);
|
||||
/* This quirk needs to be replaced by a callback-function later */
|
||||
return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ?
|
||||
!is_readonly : is_readonly;
|
||||
}
|
||||
|
||||
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
|
@ -1427,7 +1448,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|||
sdhci_finish_command(host);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
static void sdhci_show_adma_error(struct sdhci_host *host)
|
||||
{
|
||||
const char *name = mmc_hostname(host->mmc);
|
||||
|
@ -1708,7 +1729,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
|
||||
host->version = (host->version & SDHCI_SPEC_VER_MASK)
|
||||
>> SDHCI_SPEC_VER_SHIFT;
|
||||
if (host->version > SDHCI_SPEC_200) {
|
||||
if (host->version > SDHCI_SPEC_300) {
|
||||
printk(KERN_ERR "%s: Unknown controller version (%d). "
|
||||
"You may experience problems.\n", mmc_hostname(mmc),
|
||||
host->version);
|
||||
|
@ -1779,8 +1800,13 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
|
||||
}
|
||||
|
||||
host->max_clk =
|
||||
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
|
||||
if (host->version >= SDHCI_SPEC_300)
|
||||
host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
|
||||
>> SDHCI_CLOCK_BASE_SHIFT;
|
||||
else
|
||||
host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
|
||||
>> SDHCI_CLOCK_BASE_SHIFT;
|
||||
|
||||
host->max_clk *= 1000000;
|
||||
if (host->max_clk == 0 || host->quirks &
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
|
||||
|
@ -1815,18 +1841,21 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc->ops = &sdhci_ops;
|
||||
if (host->ops->get_min_clock)
|
||||
mmc->f_min = host->ops->get_min_clock(host);
|
||||
else if (host->version >= SDHCI_SPEC_300)
|
||||
mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300;
|
||||
else
|
||||
mmc->f_min = host->max_clk / 256;
|
||||
mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
|
||||
mmc->f_max = host->max_clk;
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||
|
||||
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
|
||||
if (caps & SDHCI_CAN_DO_HISPD)
|
||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
||||
mmc_card_is_removable(mmc))
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
mmc->ocr_avail = 0;
|
||||
|
@ -1850,12 +1879,11 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
* can do scatter/gather or not.
|
||||
*/
|
||||
if (host->flags & SDHCI_USE_ADMA)
|
||||
mmc->max_hw_segs = 128;
|
||||
mmc->max_segs = 128;
|
||||
else if (host->flags & SDHCI_USE_SDMA)
|
||||
mmc->max_hw_segs = 1;
|
||||
mmc->max_segs = 1;
|
||||
else /* PIO */
|
||||
mmc->max_hw_segs = 128;
|
||||
mmc->max_phys_segs = 128;
|
||||
mmc->max_segs = 128;
|
||||
|
||||
/*
|
||||
* Maximum number of sectors in one transfer. Limited by DMA boundary
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/*
|
||||
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
|
||||
*
|
||||
* Header file for Host Controller registers and I/O accessors.
|
||||
*
|
||||
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -8,14 +10,16 @@
|
|||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
#ifndef __SDHCI_H
|
||||
#define __SDHCI_H
|
||||
#ifndef __SDHCI_HW_H
|
||||
#define __SDHCI_HW_H
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <linux/mmc/sdhci.h>
|
||||
|
||||
/*
|
||||
* Controller registers
|
||||
*/
|
||||
|
@ -86,6 +90,10 @@
|
|||
|
||||
#define SDHCI_CLOCK_CONTROL 0x2C
|
||||
#define SDHCI_DIVIDER_SHIFT 8
|
||||
#define SDHCI_DIVIDER_HI_SHIFT 6
|
||||
#define SDHCI_DIV_MASK 0xFF
|
||||
#define SDHCI_DIV_MASK_LEN 8
|
||||
#define SDHCI_DIV_HI_MASK 0x300
|
||||
#define SDHCI_CLOCK_CARD_EN 0x0004
|
||||
#define SDHCI_CLOCK_INT_STABLE 0x0002
|
||||
#define SDHCI_CLOCK_INT_EN 0x0001
|
||||
|
@ -140,6 +148,7 @@
|
|||
#define SDHCI_TIMEOUT_CLK_SHIFT 0
|
||||
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
|
||||
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
|
||||
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
|
||||
#define SDHCI_CLOCK_BASE_SHIFT 8
|
||||
#define SDHCI_MAX_BLOCK_MASK 0x00030000
|
||||
#define SDHCI_MAX_BLOCK_SHIFT 16
|
||||
|
@ -178,134 +187,14 @@
|
|||
#define SDHCI_SPEC_VER_SHIFT 0
|
||||
#define SDHCI_SPEC_100 0
|
||||
#define SDHCI_SPEC_200 1
|
||||
#define SDHCI_SPEC_300 2
|
||||
|
||||
struct sdhci_ops;
|
||||
|
||||
struct sdhci_host {
|
||||
/* Data set by hardware interface driver */
|
||||
const char *hw_name; /* Hardware bus name */
|
||||
|
||||
unsigned int quirks; /* Deviations from spec. */
|
||||
|
||||
/* Controller doesn't honor resets unless we touch the clock register */
|
||||
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
|
||||
/* Controller has bad caps bits, but really supports DMA */
|
||||
#define SDHCI_QUIRK_FORCE_DMA (1<<1)
|
||||
/* Controller doesn't like to be reset when there is no card inserted. */
|
||||
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
|
||||
/* Controller doesn't like clearing the power reg before a change */
|
||||
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
|
||||
/* Controller has flaky internal state so reset it on each ios change */
|
||||
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
|
||||
/* Controller has an unusable DMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
|
||||
/* Controller has an unusable ADMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
|
||||
/* Controller can only DMA from 32-bit aligned addresses */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
|
||||
/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
|
||||
/* Controller can only ADMA chunks that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
|
||||
/* Controller needs to be reset after each request to stay stable */
|
||||
#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
|
||||
/* Controller needs voltage and power writes to happen separately */
|
||||
#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
|
||||
/* Controller provides an incorrect timeout value for transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
|
||||
/* Controller has an issue with buffer bits for small transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
|
||||
/* Controller does not provide transfer-complete interrupt when not busy */
|
||||
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
|
||||
/* Controller has unreliable card detection */
|
||||
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
|
||||
/* Controller reports inverted write-protect state */
|
||||
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
|
||||
/* Controller has nonstandard clock management */
|
||||
#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
|
||||
/* Controller does not like fast PIO transfers */
|
||||
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
|
||||
/* Controller losing signal/interrupt enable states after reset */
|
||||
#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)
|
||||
/* Controller has to be forced to use block size of 2048 bytes */
|
||||
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
|
||||
/* Controller cannot do multi-block transfers */
|
||||
#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21)
|
||||
/* Controller can only handle 1-bit data transfers */
|
||||
#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22)
|
||||
/* Controller needs 10ms delay between applying power and clock */
|
||||
#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
|
||||
/* Controller uses SDCLK instead of TMCLK for data timeouts */
|
||||
#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
|
||||
/* Controller reports wrong base clock capability */
|
||||
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
|
||||
/* Controller cannot support End Attribute in NOP ADMA descriptor */
|
||||
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
|
||||
/* Controller is missing device caps. Use caps provided by host */
|
||||
#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
|
||||
/* Controller uses Auto CMD12 command to stop the transfer */
|
||||
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
|
||||
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
|
||||
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem * ioaddr; /* Mapped address */
|
||||
|
||||
const struct sdhci_ops *ops; /* Low level hw interface */
|
||||
|
||||
struct regulator *vmmc; /* Power regulator */
|
||||
|
||||
/* Internal data */
|
||||
struct mmc_host *mmc; /* MMC structure */
|
||||
u64 dma_mask; /* custom DMA mask */
|
||||
|
||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||
struct led_classdev led; /* LED control */
|
||||
char led_name[32];
|
||||
#endif
|
||||
|
||||
spinlock_t lock; /* Mutex */
|
||||
|
||||
int flags; /* Host attributes */
|
||||
#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
|
||||
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
|
||||
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
||||
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
unsigned int max_clk; /* Max possible freq (MHz) */
|
||||
unsigned int timeout_clk; /* Timeout freq (KHz) */
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
u8 pwr; /* Current voltage */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
|
||||
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||
unsigned int blocks; /* remaining PIO blocks */
|
||||
|
||||
int sg_count; /* Mapped sg entries */
|
||||
|
||||
u8 *adma_desc; /* ADMA descriptor table */
|
||||
u8 *align_buffer; /* Bounce buffer */
|
||||
|
||||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||
|
||||
struct tasklet_struct card_tasklet; /* Tasklet structures */
|
||||
struct tasklet_struct finish_tasklet;
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
unsigned int caps; /* Alternative capabilities */
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
/*
|
||||
* End of controller registers.
|
||||
*/
|
||||
|
||||
#define SDHCI_MAX_DIV_SPEC_200 256
|
||||
#define SDHCI_MAX_DIV_SPEC_300 2046
|
||||
|
||||
struct sdhci_ops {
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
|
@ -323,6 +212,9 @@ struct sdhci_ops {
|
|||
unsigned int (*get_max_clock)(struct sdhci_host *host);
|
||||
unsigned int (*get_min_clock)(struct sdhci_host *host);
|
||||
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
|
||||
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
|
||||
u8 power_mode);
|
||||
unsigned int (*get_ro)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
|
@ -427,4 +319,4 @@ extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);
|
|||
extern int sdhci_resume_host(struct sdhci_host *host);
|
||||
#endif
|
||||
|
||||
#endif /* __SDHCI_H */
|
||||
#endif /* __SDHCI_HW_H */
|
||||
|
|
|
@ -846,8 +846,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
mmc->caps = MMC_CAP_MMC_HIGHSPEED;
|
||||
if (pd->caps)
|
||||
mmc->caps |= pd->caps;
|
||||
mmc->max_phys_segs = 128;
|
||||
mmc->max_hw_segs = 128;
|
||||
mmc->max_segs = 128;
|
||||
mmc->max_blk_size = 512;
|
||||
mmc->max_blk_count = 65535;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
|
|
@ -978,11 +978,10 @@ static int tifm_sd_probe(struct tifm_dev *sock)
|
|||
mmc->f_max = 24000000;
|
||||
|
||||
mmc->max_blk_count = 2048;
|
||||
mmc->max_hw_segs = mmc->max_blk_count;
|
||||
mmc->max_segs = mmc->max_blk_count;
|
||||
mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE);
|
||||
mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
|
||||
mmc->max_req_size = mmc->max_seg_size;
|
||||
mmc->max_phys_segs = mmc->max_hw_segs;
|
||||
|
||||
sock->card_event = tifm_sd_card_event;
|
||||
sock->data_event = tifm_sd_data_event;
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
* USB SD Host Controller (USHC) controller driver.
|
||||
*
|
||||
* Copyright (C) 2010 Cambridge Silicon Radio Ltd.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Notes:
|
||||
* - Only version 2 devices are supported.
|
||||
* - Version 2 devices only support SDIO cards/devices (R2 response is
|
||||
* unsupported).
|
||||
*
|
||||
* References:
|
||||
* [USHC] USB SD Host Controller specification (CS-118793-SP)
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
enum ushc_request {
|
||||
USHC_GET_CAPS = 0x00,
|
||||
USHC_HOST_CTRL = 0x01,
|
||||
USHC_PWR_CTRL = 0x02,
|
||||
USHC_CLK_FREQ = 0x03,
|
||||
USHC_EXEC_CMD = 0x04,
|
||||
USHC_READ_RESP = 0x05,
|
||||
USHC_RESET = 0x06,
|
||||
};
|
||||
|
||||
enum ushc_request_type {
|
||||
USHC_GET_CAPS_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
USHC_HOST_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
USHC_PWR_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
USHC_CLK_FREQ_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
USHC_EXEC_CMD_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
USHC_READ_RESP_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
USHC_RESET_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
};
|
||||
|
||||
#define USHC_GET_CAPS_VERSION_MASK 0xff
|
||||
#define USHC_GET_CAPS_3V3 (1 << 8)
|
||||
#define USHC_GET_CAPS_3V0 (1 << 9)
|
||||
#define USHC_GET_CAPS_1V8 (1 << 10)
|
||||
#define USHC_GET_CAPS_HIGH_SPD (1 << 16)
|
||||
|
||||
#define USHC_HOST_CTRL_4BIT (1 << 1)
|
||||
#define USHC_HOST_CTRL_HIGH_SPD (1 << 0)
|
||||
|
||||
#define USHC_PWR_CTRL_OFF 0x00
|
||||
#define USHC_PWR_CTRL_3V3 0x01
|
||||
#define USHC_PWR_CTRL_3V0 0x02
|
||||
#define USHC_PWR_CTRL_1V8 0x03
|
||||
|
||||
#define USHC_READ_RESP_BUSY (1 << 4)
|
||||
#define USHC_READ_RESP_ERR_TIMEOUT (1 << 3)
|
||||
#define USHC_READ_RESP_ERR_CRC (1 << 2)
|
||||
#define USHC_READ_RESP_ERR_DAT (1 << 1)
|
||||
#define USHC_READ_RESP_ERR_CMD (1 << 0)
|
||||
#define USHC_READ_RESP_ERR_MASK 0x0f
|
||||
|
||||
struct ushc_cbw {
|
||||
__u8 signature;
|
||||
__u8 cmd_idx;
|
||||
__le16 block_size;
|
||||
__le32 arg;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define USHC_CBW_SIGNATURE 'C'
|
||||
|
||||
struct ushc_csw {
|
||||
__u8 signature;
|
||||
__u8 status;
|
||||
__le32 response;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define USHC_CSW_SIGNATURE 'S'
|
||||
|
||||
struct ushc_int_data {
|
||||
u8 status;
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
#define USHC_INT_STATUS_SDIO_INT (1 << 1)
|
||||
#define USHC_INT_STATUS_CARD_PRESENT (1 << 0)
|
||||
|
||||
|
||||
struct ushc_data {
|
||||
struct usb_device *usb_dev;
|
||||
struct mmc_host *mmc;
|
||||
|
||||
struct urb *int_urb;
|
||||
struct ushc_int_data *int_data;
|
||||
|
||||
struct urb *cbw_urb;
|
||||
struct ushc_cbw *cbw;
|
||||
|
||||
struct urb *data_urb;
|
||||
|
||||
struct urb *csw_urb;
|
||||
struct ushc_csw *csw;
|
||||
|
||||
spinlock_t lock;
|
||||
struct mmc_request *current_req;
|
||||
u32 caps;
|
||||
u16 host_ctrl;
|
||||
unsigned long flags;
|
||||
u8 last_status;
|
||||
int clock_freq;
|
||||
};
|
||||
|
||||
#define DISCONNECTED 0
|
||||
#define INT_EN 1
|
||||
#define IGNORE_NEXT_INT 2
|
||||
|
||||
static void data_callback(struct urb *urb);
|
||||
|
||||
static int ushc_hw_reset(struct ushc_data *ushc)
|
||||
{
|
||||
return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0),
|
||||
USHC_RESET, USHC_RESET_TYPE,
|
||||
0, 0, NULL, 0, 100);
|
||||
}
|
||||
|
||||
static int ushc_hw_get_caps(struct ushc_data *ushc)
|
||||
{
|
||||
int ret;
|
||||
int version;
|
||||
|
||||
ret = usb_control_msg(ushc->usb_dev, usb_rcvctrlpipe(ushc->usb_dev, 0),
|
||||
USHC_GET_CAPS, USHC_GET_CAPS_TYPE,
|
||||
0, 0, &ushc->caps, sizeof(ushc->caps), 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ushc->caps = le32_to_cpu(ushc->caps);
|
||||
|
||||
version = ushc->caps & USHC_GET_CAPS_VERSION_MASK;
|
||||
if (version != 0x02) {
|
||||
dev_err(&ushc->usb_dev->dev, "controller version %d is not supported\n", version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ushc_hw_set_host_ctrl(struct ushc_data *ushc, u16 mask, u16 val)
|
||||
{
|
||||
u16 host_ctrl;
|
||||
int ret;
|
||||
|
||||
host_ctrl = (ushc->host_ctrl & ~mask) | val;
|
||||
ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0),
|
||||
USHC_HOST_CTRL, USHC_HOST_CTRL_TYPE,
|
||||
host_ctrl, 0, NULL, 0, 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ushc->host_ctrl = host_ctrl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void int_callback(struct urb *urb)
|
||||
{
|
||||
struct ushc_data *ushc = urb->context;
|
||||
u8 status, last_status;
|
||||
|
||||
if (urb->status < 0)
|
||||
return;
|
||||
|
||||
status = ushc->int_data->status;
|
||||
last_status = ushc->last_status;
|
||||
ushc->last_status = status;
|
||||
|
||||
/*
|
||||
* Ignore the card interrupt status on interrupt transfers that
|
||||
* were submitted while card interrupts where disabled.
|
||||
*
|
||||
* This avoid occasional spurious interrupts when enabling
|
||||
* interrupts immediately after clearing the source on the card.
|
||||
*/
|
||||
|
||||
if (!test_and_clear_bit(IGNORE_NEXT_INT, &ushc->flags)
|
||||
&& test_bit(INT_EN, &ushc->flags)
|
||||
&& status & USHC_INT_STATUS_SDIO_INT) {
|
||||
mmc_signal_sdio_irq(ushc->mmc);
|
||||
}
|
||||
|
||||
if ((status ^ last_status) & USHC_INT_STATUS_CARD_PRESENT)
|
||||
mmc_detect_change(ushc->mmc, msecs_to_jiffies(100));
|
||||
|
||||
if (!test_bit(INT_EN, &ushc->flags))
|
||||
set_bit(IGNORE_NEXT_INT, &ushc->flags);
|
||||
usb_submit_urb(ushc->int_urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static void cbw_callback(struct urb *urb)
|
||||
{
|
||||
struct ushc_data *ushc = urb->context;
|
||||
|
||||
if (urb->status != 0) {
|
||||
usb_unlink_urb(ushc->data_urb);
|
||||
usb_unlink_urb(ushc->csw_urb);
|
||||
}
|
||||
}
|
||||
|
||||
static void data_callback(struct urb *urb)
|
||||
{
|
||||
struct ushc_data *ushc = urb->context;
|
||||
|
||||
if (urb->status != 0)
|
||||
usb_unlink_urb(ushc->csw_urb);
|
||||
}
|
||||
|
||||
static void csw_callback(struct urb *urb)
|
||||
{
|
||||
struct ushc_data *ushc = urb->context;
|
||||
struct mmc_request *req = ushc->current_req;
|
||||
int status;
|
||||
|
||||
status = ushc->csw->status;
|
||||
|
||||
if (urb->status != 0) {
|
||||
req->cmd->error = urb->status;
|
||||
} else if (status & USHC_READ_RESP_ERR_CMD) {
|
||||
if (status & USHC_READ_RESP_ERR_CRC)
|
||||
req->cmd->error = -EIO;
|
||||
else
|
||||
req->cmd->error = -ETIMEDOUT;
|
||||
}
|
||||
if (req->data) {
|
||||
if (status & USHC_READ_RESP_ERR_DAT) {
|
||||
if (status & USHC_READ_RESP_ERR_CRC)
|
||||
req->data->error = -EIO;
|
||||
else
|
||||
req->data->error = -ETIMEDOUT;
|
||||
req->data->bytes_xfered = 0;
|
||||
} else {
|
||||
req->data->bytes_xfered = req->data->blksz * req->data->blocks;
|
||||
}
|
||||
}
|
||||
|
||||
req->cmd->resp[0] = le32_to_cpu(ushc->csw->response);
|
||||
|
||||
mmc_request_done(ushc->mmc, req);
|
||||
}
|
||||
|
||||
static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||
{
|
||||
struct ushc_data *ushc = mmc_priv(mmc);
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ushc->lock, flags);
|
||||
|
||||
if (test_bit(DISCONNECTED, &ushc->flags)) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Version 2 firmware doesn't support the R2 response format. */
|
||||
if (req->cmd->flags & MMC_RSP_136) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The Astoria's data FIFOs don't work with clock speeds < 5MHz so
|
||||
limit commands with data to 6MHz or more. */
|
||||
if (req->data && ushc->clock_freq < 6000000) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ushc->current_req = req;
|
||||
|
||||
/* Start cmd with CBW. */
|
||||
ushc->cbw->cmd_idx = cpu_to_le16(req->cmd->opcode);
|
||||
if (req->data)
|
||||
ushc->cbw->block_size = cpu_to_le16(req->data->blksz);
|
||||
else
|
||||
ushc->cbw->block_size = 0;
|
||||
ushc->cbw->arg = cpu_to_le32(req->cmd->arg);
|
||||
|
||||
ret = usb_submit_urb(ushc->cbw_urb, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* Submit data (if any). */
|
||||
if (req->data) {
|
||||
struct mmc_data *data = req->data;
|
||||
int pipe;
|
||||
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
pipe = usb_rcvbulkpipe(ushc->usb_dev, 6);
|
||||
else
|
||||
pipe = usb_sndbulkpipe(ushc->usb_dev, 2);
|
||||
|
||||
usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe,
|
||||
sg_virt(data->sg), data->sg->length,
|
||||
data_callback, ushc);
|
||||
ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Submit CSW. */
|
||||
ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&ushc->lock, flags);
|
||||
if (ret < 0) {
|
||||
usb_unlink_urb(ushc->cbw_urb);
|
||||
usb_unlink_urb(ushc->data_urb);
|
||||
req->cmd->error = ret;
|
||||
mmc_request_done(mmc, req);
|
||||
}
|
||||
}
|
||||
|
||||
static int ushc_set_power(struct ushc_data *ushc, unsigned char power_mode)
|
||||
{
|
||||
u16 voltage;
|
||||
|
||||
switch (power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
voltage = USHC_PWR_CTRL_OFF;
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
case MMC_POWER_ON:
|
||||
voltage = USHC_PWR_CTRL_3V3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0),
|
||||
USHC_PWR_CTRL, USHC_PWR_CTRL_TYPE,
|
||||
voltage, 0, NULL, 0, 100);
|
||||
}
|
||||
|
||||
static int ushc_set_bus_width(struct ushc_data *ushc, int bus_width)
|
||||
{
|
||||
return ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_4BIT,
|
||||
bus_width == 4 ? USHC_HOST_CTRL_4BIT : 0);
|
||||
}
|
||||
|
||||
static int ushc_set_bus_freq(struct ushc_data *ushc, int clk, bool enable_hs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Hardware can't detect interrupts while the clock is off. */
|
||||
if (clk == 0)
|
||||
clk = 400000;
|
||||
|
||||
ret = ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_HIGH_SPD,
|
||||
enable_hs ? USHC_HOST_CTRL_HIGH_SPD : 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0),
|
||||
USHC_CLK_FREQ, USHC_CLK_FREQ_TYPE,
|
||||
clk & 0xffff, (clk >> 16) & 0xffff, NULL, 0, 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ushc->clock_freq = clk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ushc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct ushc_data *ushc = mmc_priv(mmc);
|
||||
|
||||
ushc_set_power(ushc, ios->power_mode);
|
||||
ushc_set_bus_width(ushc, 1 << ios->bus_width);
|
||||
ushc_set_bus_freq(ushc, ios->clock, ios->timing == MMC_TIMING_SD_HS);
|
||||
}
|
||||
|
||||
static int ushc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct ushc_data *ushc = mmc_priv(mmc);
|
||||
|
||||
return !!(ushc->last_status & USHC_INT_STATUS_CARD_PRESENT);
|
||||
}
|
||||
|
||||
static void ushc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
{
|
||||
struct ushc_data *ushc = mmc_priv(mmc);
|
||||
|
||||
if (enable)
|
||||
set_bit(INT_EN, &ushc->flags);
|
||||
else
|
||||
clear_bit(INT_EN, &ushc->flags);
|
||||
}
|
||||
|
||||
static void ushc_clean_up(struct ushc_data *ushc)
|
||||
{
|
||||
usb_free_urb(ushc->int_urb);
|
||||
usb_free_urb(ushc->csw_urb);
|
||||
usb_free_urb(ushc->data_urb);
|
||||
usb_free_urb(ushc->cbw_urb);
|
||||
|
||||
kfree(ushc->int_data);
|
||||
kfree(ushc->cbw);
|
||||
kfree(ushc->csw);
|
||||
|
||||
mmc_free_host(ushc->mmc);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops ushc_ops = {
|
||||
.request = ushc_request,
|
||||
.set_ios = ushc_set_ios,
|
||||
.get_cd = ushc_get_cd,
|
||||
.enable_sdio_irq = ushc_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct mmc_host *mmc;
|
||||
struct ushc_data *ushc;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev);
|
||||
if (mmc == NULL)
|
||||
return -ENOMEM;
|
||||
ushc = mmc_priv(mmc);
|
||||
usb_set_intfdata(intf, ushc);
|
||||
|
||||
ushc->usb_dev = usb_dev;
|
||||
ushc->mmc = mmc;
|
||||
|
||||
spin_lock_init(&ushc->lock);
|
||||
|
||||
ret = ushc_hw_reset(ushc);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Read capabilities. */
|
||||
ret = ushc_hw_get_caps(ushc);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
mmc->ops = &ushc_ops;
|
||||
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 50000000;
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
||||
mmc->caps |= (ushc->caps & USHC_GET_CAPS_HIGH_SPD) ? MMC_CAP_SD_HIGHSPEED : 0;
|
||||
|
||||
mmc->max_seg_size = 512*511;
|
||||
mmc->max_segs = 1;
|
||||
mmc->max_req_size = 512*511;
|
||||
mmc->max_blk_size = 512;
|
||||
mmc->max_blk_count = 511;
|
||||
|
||||
ushc->int_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (ushc->int_urb == NULL)
|
||||
goto err;
|
||||
ushc->int_data = kzalloc(sizeof(struct ushc_int_data), GFP_KERNEL);
|
||||
if (ushc->int_data == NULL)
|
||||
goto err;
|
||||
usb_fill_int_urb(ushc->int_urb, ushc->usb_dev,
|
||||
usb_rcvintpipe(usb_dev,
|
||||
intf->cur_altsetting->endpoint[0].desc.bEndpointAddress),
|
||||
ushc->int_data, sizeof(struct ushc_int_data),
|
||||
int_callback, ushc,
|
||||
intf->cur_altsetting->endpoint[0].desc.bInterval);
|
||||
|
||||
ushc->cbw_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (ushc->cbw_urb == NULL)
|
||||
goto err;
|
||||
ushc->cbw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL);
|
||||
if (ushc->cbw == NULL)
|
||||
goto err;
|
||||
ushc->cbw->signature = USHC_CBW_SIGNATURE;
|
||||
|
||||
usb_fill_bulk_urb(ushc->cbw_urb, ushc->usb_dev, usb_sndbulkpipe(usb_dev, 2),
|
||||
ushc->cbw, sizeof(struct ushc_cbw),
|
||||
cbw_callback, ushc);
|
||||
|
||||
ushc->data_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (ushc->data_urb == NULL)
|
||||
goto err;
|
||||
|
||||
ushc->csw_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (ushc->csw_urb == NULL)
|
||||
goto err;
|
||||
ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL);
|
||||
if (ushc->csw == NULL)
|
||||
goto err;
|
||||
usb_fill_bulk_urb(ushc->csw_urb, ushc->usb_dev, usb_rcvbulkpipe(usb_dev, 6),
|
||||
ushc->csw, sizeof(struct ushc_csw),
|
||||
csw_callback, ushc);
|
||||
|
||||
ret = mmc_add_host(ushc->mmc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = usb_submit_urb(ushc->int_urb, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
mmc_remove_host(ushc->mmc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
ushc_clean_up(ushc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ushc_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct ushc_data *ushc = usb_get_intfdata(intf);
|
||||
|
||||
spin_lock_irq(&ushc->lock);
|
||||
set_bit(DISCONNECTED, &ushc->flags);
|
||||
spin_unlock_irq(&ushc->lock);
|
||||
|
||||
usb_kill_urb(ushc->int_urb);
|
||||
usb_kill_urb(ushc->cbw_urb);
|
||||
usb_kill_urb(ushc->data_urb);
|
||||
usb_kill_urb(ushc->csw_urb);
|
||||
|
||||
mmc_remove_host(ushc->mmc);
|
||||
|
||||
ushc_clean_up(ushc);
|
||||
}
|
||||
|
||||
static struct usb_device_id ushc_id_table[] = {
|
||||
/* CSR USB SD Host Controller */
|
||||
{ USB_DEVICE(0x0a12, 0x5d10) },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, ushc_id_table);
|
||||
|
||||
static struct usb_driver ushc_driver = {
|
||||
.name = "ushc",
|
||||
.id_table = ushc_id_table,
|
||||
.probe = ushc_probe,
|
||||
.disconnect = ushc_disconnect,
|
||||
};
|
||||
|
||||
static int __init ushc_init(void)
|
||||
{
|
||||
return usb_register(&ushc_driver);
|
||||
}
|
||||
module_init(ushc_init);
|
||||
|
||||
static void __exit ushc_exit(void)
|
||||
{
|
||||
usb_deregister(&ushc_driver);
|
||||
}
|
||||
module_exit(ushc_exit);
|
||||
|
||||
MODULE_DESCRIPTION("USB SD Host Controller driver");
|
||||
MODULE_AUTHOR("David Vrabel <david.vrabel@csr.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1050,8 +1050,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
|
|||
mmc->ops = &via_sdc_ops;
|
||||
|
||||
/*Hardware cannot do scatter lists*/
|
||||
mmc->max_hw_segs = 1;
|
||||
mmc->max_phys_segs = 1;
|
||||
mmc->max_segs = 1;
|
||||
|
||||
mmc->max_blk_size = VIA_CRDR_MAX_BLOCK_LENGTH;
|
||||
mmc->max_blk_count = VIA_CRDR_MAX_BLOCK_COUNT;
|
||||
|
|
|
@ -1235,8 +1235,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
|
|||
* Maximum number of segments. Worst case is one sector per segment
|
||||
* so this will be 64kB/512.
|
||||
*/
|
||||
mmc->max_hw_segs = 128;
|
||||
mmc->max_phys_segs = 128;
|
||||
mmc->max_segs = 128;
|
||||
|
||||
/*
|
||||
* Maximum request size. Also limited by 64KiB buffer.
|
||||
|
|
|
@ -48,6 +48,7 @@ struct mmc_ext_csd {
|
|||
unsigned int sa_timeout; /* Units: 100ns */
|
||||
unsigned int hs_max_dtr;
|
||||
unsigned int sectors;
|
||||
unsigned int card_type;
|
||||
unsigned int hc_erase_size; /* In sectors */
|
||||
unsigned int hc_erase_timeout; /* In milliseconds */
|
||||
unsigned int sec_trim_mult; /* Secure trim multiplier */
|
||||
|
@ -113,6 +114,7 @@ struct mmc_card {
|
|||
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
|
||||
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
|
||||
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
|
||||
#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */
|
||||
unsigned int quirks; /* card quirks */
|
||||
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
|
||||
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
|
||||
|
@ -154,11 +156,13 @@ struct mmc_card {
|
|||
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
|
||||
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
|
||||
|
||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
|
||||
#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
|
||||
|
||||
static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
|
||||
{
|
||||
|
@ -173,6 +177,8 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c)
|
|||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
||||
|
||||
#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
|
||||
|
||||
#define mmc_list_to_card(l) container_of(l, struct mmc_card, node)
|
||||
#define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev)
|
||||
#define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d)
|
||||
|
|
|
@ -153,6 +153,8 @@ extern int mmc_can_secure_erase_trim(struct mmc_card *card);
|
|||
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
||||
unsigned int nr);
|
||||
|
||||
extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
|
||||
|
||||
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
|
||||
extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
|
||||
|
||||
|
|
|
@ -50,6 +50,12 @@ struct mmc_ios {
|
|||
#define MMC_TIMING_LEGACY 0
|
||||
#define MMC_TIMING_MMC_HS 1
|
||||
#define MMC_TIMING_SD_HS 2
|
||||
|
||||
unsigned char ddr; /* dual data rate used */
|
||||
|
||||
#define MMC_SDR_MODE 0
|
||||
#define MMC_1_2V_DDR_MODE 1
|
||||
#define MMC_1_8V_DDR_MODE 2
|
||||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
|
@ -123,6 +129,7 @@ struct mmc_host {
|
|||
const struct mmc_host_ops *ops;
|
||||
unsigned int f_min;
|
||||
unsigned int f_max;
|
||||
unsigned int f_init;
|
||||
u32 ocr_avail;
|
||||
struct notifier_block pm_notify;
|
||||
|
||||
|
@ -157,13 +164,16 @@ struct mmc_host {
|
|||
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
|
||||
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
|
||||
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
|
||||
#define MMC_CAP_1_8V_DDR (1 << 11) /* can support */
|
||||
/* DDR mode at 1.8V */
|
||||
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
|
||||
/* DDR mode at 1.2V */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
/* host specific block data */
|
||||
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
|
||||
unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
|
||||
unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */
|
||||
unsigned short max_segs; /* see blk_queue_max_segments */
|
||||
unsigned short unused;
|
||||
unsigned int max_req_size; /* maximum number of bytes in one req */
|
||||
unsigned int max_blk_size; /* maximum size of one mmc block */
|
||||
|
@ -212,6 +222,10 @@ struct mmc_host {
|
|||
struct led_trigger *led; /* activity led */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
bool regulator_enabled; /* regulator state */
|
||||
#endif
|
||||
|
||||
struct dentry *debugfs_root;
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
|
@ -236,8 +250,8 @@ static inline void *mmc_priv(struct mmc_host *host)
|
|||
extern int mmc_suspend_host(struct mmc_host *);
|
||||
extern int mmc_resume_host(struct mmc_host *);
|
||||
|
||||
extern void mmc_power_save_host(struct mmc_host *host);
|
||||
extern void mmc_power_restore_host(struct mmc_host *host);
|
||||
extern int mmc_power_save_host(struct mmc_host *host);
|
||||
extern int mmc_power_restore_host(struct mmc_host *host);
|
||||
|
||||
extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
|
||||
extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
|
||||
|
@ -250,8 +264,24 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
|||
|
||||
struct regulator;
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
int mmc_regulator_get_ocrmask(struct regulator *supply);
|
||||
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit);
|
||||
#else
|
||||
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mmc_card_awake(struct mmc_host *host);
|
||||
int mmc_card_sleep(struct mmc_host *host);
|
||||
|
@ -268,5 +298,13 @@ static inline void mmc_set_disable_delay(struct mmc_host *host,
|
|||
host->disable_delay = disable_delay;
|
||||
}
|
||||
|
||||
/* Module parameter */
|
||||
extern int mmc_assume_removable;
|
||||
|
||||
static inline int mmc_card_is_removable(struct mmc_host *host)
|
||||
{
|
||||
return !(host->caps & MMC_CAP_NONREMOVABLE) && mmc_assume_removable;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -277,11 +277,19 @@ struct _mmc_csd {
|
|||
|
||||
#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
|
||||
#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
|
||||
#define EXT_CSD_CARD_TYPE_MASK 0x3 /* Mask out reserved and DDR bits */
|
||||
#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */
|
||||
/* DDR mode @1.8V or 3V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */
|
||||
/* DDR mode @1.2V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
|
||||
| EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
|
||||
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
|
||||
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
|
||||
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
|
||||
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
|
||||
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
|
||||
|
||||
#define EXT_CSD_SEC_ER_EN BIT(0)
|
||||
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
||||
|
|
|
@ -28,7 +28,7 @@ struct sdhci_host;
|
|||
struct sdhci_pltfm_data {
|
||||
struct sdhci_ops *ops;
|
||||
unsigned int quirks;
|
||||
int (*init)(struct sdhci_host *host);
|
||||
int (*init)(struct sdhci_host *host, struct sdhci_pltfm_data *pdata);
|
||||
void (*exit)(struct sdhci_host *host);
|
||||
};
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* linux/include/linux/mmc/sdhci.h - Secure Digital Host Controller Interface
|
||||
*
|
||||
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
#ifndef __SDHCI_H
|
||||
#define __SDHCI_H
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
struct sdhci_host {
|
||||
/* Data set by hardware interface driver */
|
||||
const char *hw_name; /* Hardware bus name */
|
||||
|
||||
unsigned int quirks; /* Deviations from spec. */
|
||||
|
||||
/* Controller doesn't honor resets unless we touch the clock register */
|
||||
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
|
||||
/* Controller has bad caps bits, but really supports DMA */
|
||||
#define SDHCI_QUIRK_FORCE_DMA (1<<1)
|
||||
/* Controller doesn't like to be reset when there is no card inserted. */
|
||||
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
|
||||
/* Controller doesn't like clearing the power reg before a change */
|
||||
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
|
||||
/* Controller has flaky internal state so reset it on each ios change */
|
||||
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
|
||||
/* Controller has an unusable DMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
|
||||
/* Controller has an unusable ADMA engine */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
|
||||
/* Controller can only DMA from 32-bit aligned addresses */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
|
||||
/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
|
||||
/* Controller can only ADMA chunks that are a multiple of 32 bits */
|
||||
#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
|
||||
/* Controller needs to be reset after each request to stay stable */
|
||||
#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
|
||||
/* Controller needs voltage and power writes to happen separately */
|
||||
#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
|
||||
/* Controller provides an incorrect timeout value for transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
|
||||
/* Controller has an issue with buffer bits for small transfers */
|
||||
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
|
||||
/* Controller does not provide transfer-complete interrupt when not busy */
|
||||
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
|
||||
/* Controller has unreliable card detection */
|
||||
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
|
||||
/* Controller reports inverted write-protect state */
|
||||
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
|
||||
/* Controller has nonstandard clock management */
|
||||
#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
|
||||
/* Controller does not like fast PIO transfers */
|
||||
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
|
||||
/* Controller losing signal/interrupt enable states after reset */
|
||||
#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)
|
||||
/* Controller has to be forced to use block size of 2048 bytes */
|
||||
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
|
||||
/* Controller cannot do multi-block transfers */
|
||||
#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21)
|
||||
/* Controller can only handle 1-bit data transfers */
|
||||
#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22)
|
||||
/* Controller needs 10ms delay between applying power and clock */
|
||||
#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
|
||||
/* Controller uses SDCLK instead of TMCLK for data timeouts */
|
||||
#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
|
||||
/* Controller reports wrong base clock capability */
|
||||
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
|
||||
/* Controller cannot support End Attribute in NOP ADMA descriptor */
|
||||
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
|
||||
/* Controller is missing device caps. Use caps provided by host */
|
||||
#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
|
||||
/* Controller uses Auto CMD12 command to stop the transfer */
|
||||
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
|
||||
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
|
||||
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
||||
const struct sdhci_ops *ops; /* Low level hw interface */
|
||||
|
||||
struct regulator *vmmc; /* Power regulator */
|
||||
|
||||
/* Internal data */
|
||||
struct mmc_host *mmc; /* MMC structure */
|
||||
u64 dma_mask; /* custom DMA mask */
|
||||
|
||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||
struct led_classdev led; /* LED control */
|
||||
char led_name[32];
|
||||
#endif
|
||||
|
||||
spinlock_t lock; /* Mutex */
|
||||
|
||||
int flags; /* Host attributes */
|
||||
#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
|
||||
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
|
||||
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
||||
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
unsigned int max_clk; /* Max possible freq (MHz) */
|
||||
unsigned int timeout_clk; /* Timeout freq (KHz) */
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
u8 pwr; /* Current voltage */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
|
||||
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||
unsigned int blocks; /* remaining PIO blocks */
|
||||
|
||||
int sg_count; /* Mapped sg entries */
|
||||
|
||||
u8 *adma_desc; /* ADMA descriptor table */
|
||||
u8 *align_buffer; /* Bounce buffer */
|
||||
|
||||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||
|
||||
struct tasklet_struct card_tasklet; /* Tasklet structures */
|
||||
struct tasklet_struct finish_tasklet;
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
unsigned int caps; /* Alternative capabilities */
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
#endif /* __SDHCI_H */
|
|
@ -2430,6 +2430,13 @@
|
|||
#define PCI_DEVICE_ID_INTEL_82375 0x0482
|
||||
#define PCI_DEVICE_ID_INTEL_82424 0x0483
|
||||
#define PCI_DEVICE_ID_INTEL_82378 0x0484
|
||||
#define PCI_DEVICE_ID_INTEL_MRST_SD0 0x0807
|
||||
#define PCI_DEVICE_ID_INTEL_MRST_SD1 0x0808
|
||||
#define PCI_DEVICE_ID_INTEL_MFD_SD 0x0820
|
||||
#define PCI_DEVICE_ID_INTEL_MFD_SDIO1 0x0821
|
||||
#define PCI_DEVICE_ID_INTEL_MFD_SDIO2 0x0822
|
||||
#define PCI_DEVICE_ID_INTEL_MFD_EMMC0 0x0823
|
||||
#define PCI_DEVICE_ID_INTEL_MFD_EMMC1 0x0824
|
||||
#define PCI_DEVICE_ID_INTEL_I960 0x0960
|
||||
#define PCI_DEVICE_ID_INTEL_I960RM 0x0962
|
||||
#define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062
|
||||
|
|
Loading…
Reference in New Issue