Merge commit 'upstream/master'

This commit is contained in:
Haavard Skinnemoen
2008-07-27 13:54:08 +02:00
4878 changed files with 320543 additions and 187270 deletions
+221 -4
View File
@@ -21,13 +21,17 @@
#define RESULT_UNSUP_HOST 2
#define RESULT_UNSUP_CARD 3
#define BUFFER_SIZE (PAGE_SIZE * 4)
#define BUFFER_ORDER 2
#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
struct mmc_test_card {
struct mmc_card *card;
u8 scratch[BUFFER_SIZE];
u8 *buffer;
#ifdef CONFIG_HIGHMEM
struct page *highmem;
#endif
};
/*******************************************************************/
@@ -384,14 +388,16 @@ static int mmc_test_transfer(struct mmc_test_card *test,
int ret, i;
unsigned long flags;
BUG_ON(blocks * blksz > BUFFER_SIZE);
if (write) {
for (i = 0;i < blocks * blksz;i++)
test->scratch[i] = i;
} else {
memset(test->scratch, 0, BUFFER_SIZE);
memset(test->scratch, 0, blocks * blksz);
}
local_irq_save(flags);
sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
sg_copy_from_buffer(sg, sg_len, test->scratch, blocks * blksz);
local_irq_restore(flags);
ret = mmc_test_set_blksize(test, blksz);
@@ -438,7 +444,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
}
} else {
local_irq_save(flags);
sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
sg_copy_to_buffer(sg, sg_len, test->scratch, blocks * blksz);
local_irq_restore(flags);
for (i = 0;i < blocks * blksz;i++) {
if (test->scratch[i] != (u8)i)
@@ -799,6 +805,157 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
return 0;
}
static int mmc_test_bigsg_write(struct mmc_test_card *test)
{
int ret;
unsigned int size;
struct scatterlist sg;
if (test->card->host->max_blk_count == 1)
return RESULT_UNSUP_HOST;
size = PAGE_SIZE * 2;
size = min(size, test->card->host->max_req_size);
size = min(size, test->card->host->max_seg_size);
size = min(size, test->card->host->max_blk_count * 512);
memset(test->buffer, 0, BUFFER_SIZE);
if (size < 1024)
return RESULT_UNSUP_HOST;
sg_init_table(&sg, 1);
sg_init_one(&sg, test->buffer, BUFFER_SIZE);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
if (ret)
return ret;
return 0;
}
static int mmc_test_bigsg_read(struct mmc_test_card *test)
{
int ret, i;
unsigned int size;
struct scatterlist sg;
if (test->card->host->max_blk_count == 1)
return RESULT_UNSUP_HOST;
size = PAGE_SIZE * 2;
size = min(size, test->card->host->max_req_size);
size = min(size, test->card->host->max_seg_size);
size = min(size, test->card->host->max_blk_count * 512);
if (size < 1024)
return RESULT_UNSUP_HOST;
memset(test->buffer, 0xCD, BUFFER_SIZE);
sg_init_table(&sg, 1);
sg_init_one(&sg, test->buffer, BUFFER_SIZE);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
if (ret)
return ret;
/* mmc_test_transfer() doesn't check for read overflows */
for (i = size;i < BUFFER_SIZE;i++) {
if (test->buffer[i] != 0xCD)
return RESULT_FAIL;
}
return 0;
}
#ifdef CONFIG_HIGHMEM
static int mmc_test_write_high(struct mmc_test_card *test)
{
int ret;
struct scatterlist sg;
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, 512, 0);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
if (ret)
return ret;
return 0;
}
static int mmc_test_read_high(struct mmc_test_card *test)
{
int ret;
struct scatterlist sg;
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, 512, 0);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
if (ret)
return ret;
return 0;
}
static int mmc_test_multi_write_high(struct mmc_test_card *test)
{
int ret;
unsigned int size;
struct scatterlist sg;
if (test->card->host->max_blk_count == 1)
return RESULT_UNSUP_HOST;
size = PAGE_SIZE * 2;
size = min(size, test->card->host->max_req_size);
size = min(size, test->card->host->max_seg_size);
size = min(size, test->card->host->max_blk_count * 512);
if (size < 1024)
return RESULT_UNSUP_HOST;
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, size, 0);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
if (ret)
return ret;
return 0;
}
static int mmc_test_multi_read_high(struct mmc_test_card *test)
{
int ret;
unsigned int size;
struct scatterlist sg;
if (test->card->host->max_blk_count == 1)
return RESULT_UNSUP_HOST;
size = PAGE_SIZE * 2;
size = min(size, test->card->host->max_req_size);
size = min(size, test->card->host->max_seg_size);
size = min(size, test->card->host->max_blk_count * 512);
if (size < 1024)
return RESULT_UNSUP_HOST;
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, size, 0);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
if (ret)
return ret;
return 0;
}
#endif /* CONFIG_HIGHMEM */
static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (no data verification)",
@@ -913,6 +1070,53 @@ static const struct mmc_test_case mmc_test_cases[] = {
.name = "Correct xfer_size at read (midway failure)",
.run = mmc_test_multi_xfersize_read,
},
{
.name = "Over-sized SG list write",
.prepare = mmc_test_prepare_write,
.run = mmc_test_bigsg_write,
.cleanup = mmc_test_cleanup,
},
{
.name = "Over-sized SG list read",
.prepare = mmc_test_prepare_read,
.run = mmc_test_bigsg_read,
.cleanup = mmc_test_cleanup,
},
#ifdef CONFIG_HIGHMEM
{
.name = "Highmem write",
.prepare = mmc_test_prepare_write,
.run = mmc_test_write_high,
.cleanup = mmc_test_cleanup,
},
{
.name = "Highmem read",
.prepare = mmc_test_prepare_read,
.run = mmc_test_read_high,
.cleanup = mmc_test_cleanup,
},
{
.name = "Multi-block highmem write",
.prepare = mmc_test_prepare_write,
.run = mmc_test_multi_write_high,
.cleanup = mmc_test_cleanup,
},
{
.name = "Multi-block highmem read",
.prepare = mmc_test_prepare_read,
.run = mmc_test_multi_read_high,
.cleanup = mmc_test_cleanup,
},
#endif /* CONFIG_HIGHMEM */
};
static struct mutex mmc_test_lock;
@@ -1014,12 +1218,23 @@ static ssize_t mmc_test_store(struct device *dev,
test->card = card;
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
#ifdef CONFIG_HIGHMEM
test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
#endif
#ifdef CONFIG_HIGHMEM
if (test->buffer && test->highmem) {
#else
if (test->buffer) {
#endif
mutex_lock(&mmc_test_lock);
mmc_test_run(test, testcase);
mutex_unlock(&mmc_test_lock);
}
#ifdef CONFIG_HIGHMEM
__free_pages(test->highmem, BUFFER_ORDER);
#endif
kfree(test->buffer);
kfree(test);
@@ -1041,6 +1256,8 @@ static int mmc_test_probe(struct mmc_card *card)
if (ret)
return ret;
dev_info(&card->dev, "Card claimed for testing.\n");
return 0;
}
+31 -66
View File
@@ -148,7 +148,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
printk(KERN_WARNING "%s: unable to allocate "
"bounce buffer\n", mmc_card_name(card));
} else {
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH);
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
blk_queue_max_sectors(mq->queue, bouncesz / 512);
blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
blk_queue_max_hw_segments(mq->queue, bouncesz / 512);
@@ -290,55 +290,15 @@ void mmc_queue_resume(struct mmc_queue *mq)
}
}
static void copy_sg(struct scatterlist *dst, unsigned int dst_len,
struct scatterlist *src, unsigned int src_len)
{
unsigned int chunk;
char *dst_buf, *src_buf;
unsigned int dst_size, src_size;
dst_buf = NULL;
src_buf = NULL;
dst_size = 0;
src_size = 0;
while (src_len) {
BUG_ON(dst_len == 0);
if (dst_size == 0) {
dst_buf = sg_virt(dst);
dst_size = dst->length;
}
if (src_size == 0) {
src_buf = sg_virt(src);
src_size = src->length;
}
chunk = min(dst_size, src_size);
memcpy(dst_buf, src_buf, chunk);
dst_buf += chunk;
src_buf += chunk;
dst_size -= chunk;
src_size -= chunk;
if (dst_size == 0) {
dst++;
dst_len--;
}
if (src_size == 0) {
src++;
src_len--;
}
}
}
/*
* Prepare the sg list(s) to be handed of to the host driver
*/
unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
{
unsigned int sg_len;
size_t buflen;
struct scatterlist *sg;
int i;
if (!mq->bounce_buf)
return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
@@ -349,47 +309,52 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
mq->bounce_sg_len = sg_len;
/*
* Shortcut in the event we only get a single entry.
*/
if (sg_len == 1) {
memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist));
return 1;
}
buflen = 0;
for_each_sg(mq->bounce_sg, sg, sg_len, i)
buflen += sg->length;
sg_init_one(mq->sg, mq->bounce_buf, 0);
while (sg_len) {
mq->sg[0].length += mq->bounce_sg[sg_len - 1].length;
sg_len--;
}
sg_init_one(mq->sg, mq->bounce_buf, buflen);
return 1;
}
/*
* If writing, bounce the data to the buffer before the request
* is sent to the host driver
*/
void mmc_queue_bounce_pre(struct mmc_queue *mq)
{
unsigned long flags;
if (!mq->bounce_buf)
return;
if (mq->bounce_sg_len == 1)
return;
if (rq_data_dir(mq->req) != WRITE)
return;
copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len);
local_irq_save(flags);
sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
mq->bounce_buf, mq->sg[0].length);
local_irq_restore(flags);
}
/*
* If reading, bounce the data from the buffer after the request
* has been handled by the host driver
*/
void mmc_queue_bounce_post(struct mmc_queue *mq)
{
unsigned long flags;
if (!mq->bounce_buf)
return;
if (mq->bounce_sg_len == 1)
return;
if (rq_data_dir(mq->req) != READ)
return;
copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1);
local_irq_save(flags);
sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
mq->bounce_buf, mq->sg[0].length);
local_irq_restore(flags);
}
+1
View File
@@ -12,3 +12,4 @@ mmc_core-y := core.o bus.o host.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
+8
View File
@@ -252,6 +252,10 @@ int mmc_add_card(struct mmc_card *card)
if (ret)
return ret;
#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
#endif
mmc_card_set_present(card);
return 0;
@@ -263,6 +267,10 @@ int mmc_add_card(struct mmc_card *card)
*/
void mmc_remove_card(struct mmc_card *card)
{
#ifdef CONFIG_DEBUG_FS
mmc_remove_card_debugfs(card);
#endif
if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: SPI card removed\n",
+7
View File
@@ -52,5 +52,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
extern int use_spi_crc;
/* Debugfs information for hosts and cards */
void mmc_add_host_debugfs(struct mmc_host *host);
void mmc_remove_host_debugfs(struct mmc_host *host);
void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card);
#endif
+225
View File
@@ -0,0 +1,225 @@
/*
* Debugfs support for hosts and cards
*
* Copyright (C) 2008 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "core.h"
#include "mmc_ops.h"
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
static int mmc_ios_show(struct seq_file *s, void *data)
{
static const char *vdd_str[] = {
[8] = "2.0",
[9] = "2.1",
[10] = "2.2",
[11] = "2.3",
[12] = "2.4",
[13] = "2.5",
[14] = "2.6",
[15] = "2.7",
[16] = "2.8",
[17] = "2.9",
[18] = "3.0",
[19] = "3.1",
[20] = "3.2",
[21] = "3.3",
[22] = "3.4",
[23] = "3.5",
[24] = "3.6",
};
struct mmc_host *host = s->private;
struct mmc_ios *ios = &host->ios;
const char *str;
seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
seq_printf(s, "vdd:\t\t%u ", ios->vdd);
if ((1 << ios->vdd) & MMC_VDD_165_195)
seq_printf(s, "(1.65 - 1.95 V)\n");
else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
&& vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
vdd_str[ios->vdd + 1]);
else
seq_printf(s, "(invalid)\n");
switch (ios->bus_mode) {
case MMC_BUSMODE_OPENDRAIN:
str = "open drain";
break;
case MMC_BUSMODE_PUSHPULL:
str = "push-pull";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
switch (ios->chip_select) {
case MMC_CS_DONTCARE:
str = "don't care";
break;
case MMC_CS_HIGH:
str = "active high";
break;
case MMC_CS_LOW:
str = "active low";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
switch (ios->power_mode) {
case MMC_POWER_OFF:
str = "off";
break;
case MMC_POWER_UP:
str = "up";
break;
case MMC_POWER_ON:
str = "on";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
seq_printf(s, "bus width:\t%u (%u bits)\n",
ios->bus_width, 1 << ios->bus_width);
switch (ios->timing) {
case MMC_TIMING_LEGACY:
str = "legacy";
break;
case MMC_TIMING_MMC_HS:
str = "mmc high-speed";
break;
case MMC_TIMING_SD_HS:
str = "sd high-speed";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
return 0;
}
static int mmc_ios_open(struct inode *inode, struct file *file)
{
return single_open(file, mmc_ios_show, inode->i_private);
}
static const struct file_operations mmc_ios_fops = {
.open = mmc_ios_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
root = debugfs_create_dir(mmc_hostname(host), NULL);
if (IS_ERR(root))
/* Don't complain -- debugfs just isn't enabled */
return;
if (!root)
/* Complain -- debugfs is enabled, but it failed to
* create the directory. */
goto err_root;
host->debugfs_root = root;
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
goto err_ios;
return;
err_ios:
debugfs_remove_recursive(root);
host->debugfs_root = NULL;
err_root:
dev_err(&host->class_dev, "failed to initialize debugfs\n");
}
void mmc_remove_host_debugfs(struct mmc_host *host)
{
debugfs_remove_recursive(host->debugfs_root);
}
static int mmc_dbg_card_status_get(void *data, u64 *val)
{
struct mmc_card *card = data;
u32 status;
int ret;
mmc_claim_host(card->host);
ret = mmc_send_status(data, &status);
if (!ret)
*val = status;
mmc_release_host(card->host);
return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
NULL, "%08llx\n");
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
struct dentry *root;
if (!host->debugfs_root)
return;
root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
if (IS_ERR(root))
/* Don't complain -- debugfs just isn't enabled */
return;
if (!root)
/* Complain -- debugfs is enabled, but it failed to
* create the directory. */
goto err;
card->debugfs_root = root;
if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
goto err;
if (mmc_card_mmc(card) || mmc_card_sd(card))
if (!debugfs_create_file("status", S_IRUSR, root, card,
&mmc_dbg_card_status_fops))
goto err;
return;
err:
debugfs_remove_recursive(root);
card->debugfs_root = NULL;
dev_err(&card->dev, "failed to initialize debugfs\n");
}
void mmc_remove_card_debugfs(struct mmc_card *card)
{
debugfs_remove_recursive(card->debugfs_root);
}
+8
View File
@@ -127,6 +127,10 @@ int mmc_add_host(struct mmc_host *host)
if (err)
return err;
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
mmc_start_host(host);
return 0;
@@ -146,6 +150,10 @@ void mmc_remove_host(struct mmc_host *host)
{
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
mmc_remove_host_debugfs(host);
#endif
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);
+2
View File
@@ -82,6 +82,8 @@
# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
#define MCI_REGS_SIZE 0x100
/* Register access macros */
#define mci_readl(port,reg) \
__raw_readl((port)->regs + MCI_##reg)
+189
View File
@@ -9,6 +9,7 @@
*/
#include <linux/blkdev.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -17,6 +18,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/mmc/host.h>
@@ -89,6 +92,188 @@ struct atmel_mci {
#define atmci_clear_pending(host, event) \
clear_bit(event, &host->pending_events)
/*
* The debugfs stuff below is mostly optimized away when
* CONFIG_DEBUG_FS is not set.
*/
static int atmci_req_show(struct seq_file *s, void *v)
{
struct atmel_mci *host = s->private;
struct mmc_request *mrq = host->mrq;
struct mmc_command *cmd;
struct mmc_command *stop;
struct mmc_data *data;
/* Make sure we get a consistent snapshot */
spin_lock_irq(&host->mmc->lock);
if (mrq) {
cmd = mrq->cmd;
data = mrq->data;
stop = mrq->stop;
if (cmd)
seq_printf(s,
"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
cmd->opcode, cmd->arg, cmd->flags,
cmd->resp[0], cmd->resp[1], cmd->resp[2],
cmd->resp[2], cmd->error);
if (data)
seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
data->bytes_xfered, data->blocks,
data->blksz, data->flags, data->error);
if (stop)
seq_printf(s,
"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
stop->opcode, stop->arg, stop->flags,
stop->resp[0], stop->resp[1], stop->resp[2],
stop->resp[2], stop->error);
}
spin_unlock_irq(&host->mmc->lock);
return 0;
}
static int atmci_req_open(struct inode *inode, struct file *file)
{
return single_open(file, atmci_req_show, inode->i_private);
}
static const struct file_operations atmci_req_fops = {
.owner = THIS_MODULE,
.open = atmci_req_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void atmci_show_status_reg(struct seq_file *s,
const char *regname, u32 value)
{
static const char *sr_bit[] = {
[0] = "CMDRDY",
[1] = "RXRDY",
[2] = "TXRDY",
[3] = "BLKE",
[4] = "DTIP",
[5] = "NOTBUSY",
[8] = "SDIOIRQA",
[9] = "SDIOIRQB",
[16] = "RINDE",
[17] = "RDIRE",
[18] = "RCRCE",
[19] = "RENDE",
[20] = "RTOE",
[21] = "DCRCE",
[22] = "DTOE",
[30] = "OVRE",
[31] = "UNRE",
};
unsigned int i;
seq_printf(s, "%s:\t0x%08x", regname, value);
for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
if (value & (1 << i)) {
if (sr_bit[i])
seq_printf(s, " %s", sr_bit[i]);
else
seq_puts(s, " UNKNOWN");
}
}
seq_putc(s, '\n');
}
static int atmci_regs_show(struct seq_file *s, void *v)
{
struct atmel_mci *host = s->private;
u32 *buf;
buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Grab a more or less consistent snapshot */
spin_lock_irq(&host->mmc->lock);
memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
spin_unlock_irq(&host->mmc->lock);
seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
buf[MCI_MR / 4],
buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
buf[MCI_MR / 4] & 0xff);
seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
buf[MCI_BLKR / 4],
buf[MCI_BLKR / 4] & 0xffff,
(buf[MCI_BLKR / 4] >> 16) & 0xffff);
/* Don't read RSPR and RDR; it will consume the data there */
atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
return 0;
}
static int atmci_regs_open(struct inode *inode, struct file *file)
{
return single_open(file, atmci_regs_show, inode->i_private);
}
static const struct file_operations atmci_regs_fops = {
.owner = THIS_MODULE,
.open = atmci_regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void atmci_init_debugfs(struct atmel_mci *host)
{
struct mmc_host *mmc;
struct dentry *root;
struct dentry *node;
struct resource *res;
mmc = host->mmc;
root = mmc->debugfs_root;
if (!root)
return;
node = debugfs_create_file("regs", S_IRUSR, root, host,
&atmci_regs_fops);
if (IS_ERR(node))
return;
if (!node)
goto err;
res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
node->d_inode->i_size = res->end - res->start + 1;
node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops);
if (!node)
goto err;
node = debugfs_create_x32("pending_events", S_IRUSR, root,
(u32 *)&host->pending_events);
if (!node)
goto err;
node = debugfs_create_x32("completed_events", S_IRUSR, root,
(u32 *)&host->completed_events);
if (!node)
goto err;
return;
err:
dev_err(&host->pdev->dev,
"failed to initialize debugfs for controller\n");
}
static void atmci_enable(struct atmel_mci *host)
{
@@ -906,6 +1091,8 @@ static int __init atmci_probe(struct platform_device *pdev)
"Atmel MCI controller at 0x%08lx irq %d\n",
host->mapbase, irq);
atmci_init_debugfs(host);
return 0;
err_request_irq:
@@ -924,6 +1111,8 @@ static int __exit atmci_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
if (host) {
/* Debugfs stuff is cleaned up by mmc core */
if (host->detect_pin >= 0) {
int pin = host->detect_pin;
+42 -12
View File
@@ -1043,7 +1043,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
goto out6;
}
platform_set_drvdata(pdev, mmc);
platform_set_drvdata(pdev, host);
printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
" (mode=%s)\n", pdev->id, host->iobase,
@@ -1087,13 +1087,10 @@ out0:
static int __devexit au1xmmc_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct au1xmmc_host *host;
struct au1xmmc_host *host = platform_get_drvdata(pdev);
if (mmc) {
host = mmc_priv(mmc);
mmc_remove_host(mmc);
if (host) {
mmc_remove_host(host->mmc);
#ifdef CONFIG_LEDS_CLASS
if (host->platdata && host->platdata->led)
@@ -1101,8 +1098,8 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
#endif
if (host->platdata && host->platdata->cd_setup &&
!(mmc->caps & MMC_CAP_NEEDS_POLL))
host->platdata->cd_setup(mmc, 0);
!(host->mmc->caps & MMC_CAP_NEEDS_POLL))
host->platdata->cd_setup(host->mmc, 0);
au_writel(0, HOST_ENABLE(host));
au_writel(0, HOST_CONFIG(host));
@@ -1122,16 +1119,49 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
release_resource(host->ioarea);
kfree(host->ioarea);
mmc_free_host(mmc);
mmc_free_host(host->mmc);
platform_set_drvdata(pdev, NULL);
}
return 0;
}
#ifdef CONFIG_PM
static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct au1xmmc_host *host = platform_get_drvdata(pdev);
int ret;
ret = mmc_suspend_host(host->mmc, state);
if (ret)
return ret;
au_writel(0, HOST_CONFIG2(host));
au_writel(0, HOST_CONFIG(host));
au_writel(0xffffffff, HOST_STATUS(host));
au_writel(0, HOST_ENABLE(host));
au_sync();
return 0;
}
static int au1xmmc_resume(struct platform_device *pdev)
{
struct au1xmmc_host *host = platform_get_drvdata(pdev);
au1xmmc_reset_controller(host);
return mmc_resume_host(host->mmc);
}
#else
#define au1xmmc_suspend NULL
#define au1xmmc_resume NULL
#endif
static struct platform_driver au1xmmc_driver = {
.probe = au1xmmc_probe,
.remove = au1xmmc_remove,
.suspend = NULL,
.resume = NULL,
.suspend = au1xmmc_suspend,
.resume = au1xmmc_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+15 -35
View File
@@ -26,12 +26,6 @@
*
*/
#ifdef CONFIG_MMC_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
@@ -907,31 +901,12 @@ static const struct mmc_host_ops imxmci_ops = {
.get_ro = imxmci_get_ro,
};
static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
{
int i;
for (i = 0; i < dev->num_resources; i++)
if (dev->resource[i].flags == mask && nr-- == 0)
return &dev->resource[i];
return NULL;
}
static int platform_device_irq(struct platform_device *dev, int nr)
{
int i;
for (i = 0; i < dev->num_resources; i++)
if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)
return dev->resource[i].start;
return NO_IRQ;
}
static void imxmci_check_status(unsigned long data)
{
struct imxmci_host *host = (struct imxmci_host *)data;
if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) {
if (host->pdata && host->pdata->card_present &&
host->pdata->card_present(mmc_dev(host->mmc)) != host->present) {
host->present ^= 1;
dev_info(mmc_dev(host->mmc), "card %s\n",
host->present ? "inserted" : "removed");
@@ -962,13 +937,12 @@ static int imxmci_probe(struct platform_device *pdev)
printk(KERN_INFO "i.MX mmc driver\n");
r = platform_device_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_device_irq(pdev, 0);
if (!r || irq == NO_IRQ)
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!r || irq < 0)
return -ENXIO;
r = request_mem_region(r->start, 0x100, "IMXMCI");
if (!r)
if (!request_mem_region(r->start, 0x100, pdev->name))
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
@@ -995,6 +969,8 @@ static int imxmci_probe(struct platform_device *pdev)
host->mmc = mmc;
host->dma_allocated = 0;
host->pdata = pdev->dev.platform_data;
if (!host->pdata)
dev_warn(&pdev->dev, "No platform data provided!\n");
spin_lock_init(&host->lock);
host->res = r;
@@ -1047,7 +1023,11 @@ static int imxmci_probe(struct platform_device *pdev)
if (ret)
goto out;
host->present = host->pdata->card_present(mmc_dev(mmc));
if (host->pdata && host->pdata->card_present)
host->present = host->pdata->card_present(mmc_dev(mmc));
else /* if there is no way to detect assume that card is present */
host->present = 1;
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
host->timer.function = imxmci_check_status;
@@ -1073,7 +1053,7 @@ out:
}
if (mmc)
mmc_free_host(mmc);
release_resource(r);
release_mem_region(r->start, 0x100);
return ret;
}
@@ -1102,7 +1082,7 @@ static int imxmci_remove(struct platform_device *pdev)
clk_disable(host->clk);
clk_put(host->clk);
release_resource(host->res);
release_mem_region(host->res->start, 0x100);
mmc_free_host(mmc);
}
+2 -1
View File
@@ -1076,6 +1076,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
*/
if (canpower && ios->power_mode == MMC_POWER_OFF) {
int mres;
u8 nullbyte = 0;
host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
mres = spi_setup(host->spi);
@@ -1083,7 +1084,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_dbg(&host->spi->dev,
"switch to SPI mode 0 failed\n");
if (spi_w8r8(host->spi, 0x00) < 0)
if (spi_write(host->spi, &nullbyte, 1) < 0)
dev_dbg(&host->spi->dev,
"put spi signals to low failed\n");
+1 -1
View File
@@ -177,7 +177,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
if (dalgn)
DALGN |= (1 << host->dma);
else
DALGN &= (1 << host->dma);
DALGN &= ~(1 << host->dma);
DDADR(host->dma) = host->sg_dma;
DCSR(host->dma) = DCSR_RUN;
}
+31 -19
View File
@@ -1331,21 +1331,30 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
return ret;
}
static void s3cmci_shutdown(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct s3cmci_host *host = mmc_priv(mmc);
if (host->irq_cd >= 0)
free_irq(host->irq_cd, host);
mmc_remove_host(mmc);
clk_disable(host->clk);
}
static int __devexit s3cmci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct s3cmci_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
s3cmci_shutdown(pdev);
clk_disable(host->clk);
clk_put(host->clk);
tasklet_disable(&host->pio_tasklet);
s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
if (host->irq_cd >= 0)
free_irq(host->irq_cd, host);
free_irq(host->irq, host);
iounmap(host->base);
@@ -1355,17 +1364,17 @@ static int __devexit s3cmci_remove(struct platform_device *pdev)
return 0;
}
static int __devinit s3cmci_probe_2410(struct platform_device *dev)
static int __devinit s3cmci_2410_probe(struct platform_device *dev)
{
return s3cmci_probe(dev, 0);
}
static int __devinit s3cmci_probe_2412(struct platform_device *dev)
static int __devinit s3cmci_2412_probe(struct platform_device *dev)
{
return s3cmci_probe(dev, 1);
}
static int __devinit s3cmci_probe_2440(struct platform_device *dev)
static int __devinit s3cmci_2440_probe(struct platform_device *dev)
{
return s3cmci_probe(dev, 1);
}
@@ -1392,29 +1401,32 @@ static int s3cmci_resume(struct platform_device *dev)
#endif /* CONFIG_PM */
static struct platform_driver s3cmci_driver_2410 = {
static struct platform_driver s3cmci_2410_driver = {
.driver.name = "s3c2410-sdi",
.driver.owner = THIS_MODULE,
.probe = s3cmci_probe_2410,
.probe = s3cmci_2410_probe,
.remove = __devexit_p(s3cmci_remove),
.shutdown = s3cmci_shutdown,
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
static struct platform_driver s3cmci_driver_2412 = {
static struct platform_driver s3cmci_2412_driver = {
.driver.name = "s3c2412-sdi",
.driver.owner = THIS_MODULE,
.probe = s3cmci_probe_2412,
.probe = s3cmci_2412_probe,
.remove = __devexit_p(s3cmci_remove),
.shutdown = s3cmci_shutdown,
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
static struct platform_driver s3cmci_driver_2440 = {
static struct platform_driver s3cmci_2440_driver = {
.driver.name = "s3c2440-sdi",
.driver.owner = THIS_MODULE,
.probe = s3cmci_probe_2440,
.probe = s3cmci_2440_probe,
.remove = __devexit_p(s3cmci_remove),
.shutdown = s3cmci_shutdown,
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
@@ -1422,17 +1434,17 @@ static struct platform_driver s3cmci_driver_2440 = {
static int __init s3cmci_init(void)
{
platform_driver_register(&s3cmci_driver_2410);
platform_driver_register(&s3cmci_driver_2412);
platform_driver_register(&s3cmci_driver_2440);
platform_driver_register(&s3cmci_2410_driver);
platform_driver_register(&s3cmci_2412_driver);
platform_driver_register(&s3cmci_2440_driver);
return 0;
}
static void __exit s3cmci_exit(void)
{
platform_driver_unregister(&s3cmci_driver_2410);
platform_driver_unregister(&s3cmci_driver_2412);
platform_driver_unregister(&s3cmci_driver_2440);
platform_driver_unregister(&s3cmci_2410_driver);
platform_driver_unregister(&s3cmci_2412_driver);
platform_driver_unregister(&s3cmci_2440_driver);
}
module_init(s3cmci_init);
+75 -92
View File
@@ -173,119 +173,95 @@ static void sdhci_led_control(struct led_classdev *led,
* *
\*****************************************************************************/
static inline char* sdhci_sg_to_buffer(struct sdhci_host* host)
{
return sg_virt(host->cur_sg);
}
static inline int sdhci_next_sg(struct sdhci_host* host)
{
/*
* Skip to next SG entry.
*/
host->cur_sg++;
host->num_sg--;
/*
* Any entries left?
*/
if (host->num_sg > 0) {
host->offset = 0;
host->remain = host->cur_sg->length;
}
return host->num_sg;
}
static void sdhci_read_block_pio(struct sdhci_host *host)
{
int blksize, chunk_remain;
u32 data;
char *buffer;
int size;
unsigned long flags;
size_t blksize, len, chunk;
u32 scratch;
u8 *buf;
DBG("PIO reading\n");
blksize = host->data->blksz;
chunk_remain = 0;
data = 0;
chunk = 0;
buffer = sdhci_sg_to_buffer(host) + host->offset;
local_irq_save(flags);
while (blksize) {
if (chunk_remain == 0) {
data = readl(host->ioaddr + SDHCI_BUFFER);
chunk_remain = min(blksize, 4);
}
if (!sg_miter_next(&host->sg_miter))
BUG();
size = min(host->remain, chunk_remain);
len = min(host->sg_miter.length, blksize);
chunk_remain -= size;
blksize -= size;
host->offset += size;
host->remain -= size;
blksize -= len;
host->sg_miter.consumed = len;
while (size) {
*buffer = data & 0xFF;
buffer++;
data >>= 8;
size--;
}
buf = host->sg_miter.addr;
if (host->remain == 0) {
if (sdhci_next_sg(host) == 0) {
BUG_ON(blksize != 0);
return;
while (len) {
if (chunk == 0) {
scratch = readl(host->ioaddr + SDHCI_BUFFER);
chunk = 4;
}
buffer = sdhci_sg_to_buffer(host);
*buf = scratch & 0xFF;
buf++;
scratch >>= 8;
chunk--;
len--;
}
}
sg_miter_stop(&host->sg_miter);
local_irq_restore(flags);
}
static void sdhci_write_block_pio(struct sdhci_host *host)
{
int blksize, chunk_remain;
u32 data;
char *buffer;
int bytes, size;
unsigned long flags;
size_t blksize, len, chunk;
u32 scratch;
u8 *buf;
DBG("PIO writing\n");
blksize = host->data->blksz;
chunk_remain = 4;
data = 0;
chunk = 0;
scratch = 0;
bytes = 0;
buffer = sdhci_sg_to_buffer(host) + host->offset;
local_irq_save(flags);
while (blksize) {
size = min(host->remain, chunk_remain);
if (!sg_miter_next(&host->sg_miter))
BUG();
chunk_remain -= size;
blksize -= size;
host->offset += size;
host->remain -= size;
len = min(host->sg_miter.length, blksize);
while (size) {
data >>= 8;
data |= (u32)*buffer << 24;
buffer++;
size--;
}
blksize -= len;
host->sg_miter.consumed = len;
if (chunk_remain == 0) {
writel(data, host->ioaddr + SDHCI_BUFFER);
chunk_remain = min(blksize, 4);
}
buf = host->sg_miter.addr;
if (host->remain == 0) {
if (sdhci_next_sg(host) == 0) {
BUG_ON(blksize != 0);
return;
while (len) {
scratch |= (u32)*buf << (chunk * 8);
buf++;
chunk++;
len--;
if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
writel(scratch, host->ioaddr + SDHCI_BUFFER);
chunk = 0;
scratch = 0;
}
buffer = sdhci_sg_to_buffer(host);
}
}
sg_miter_stop(&host->sg_miter);
local_irq_restore(flags);
}
static void sdhci_transfer_pio(struct sdhci_host *host)
@@ -294,7 +270,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
BUG_ON(!host->data);
if (host->num_sg == 0)
if (host->blocks == 0)
return;
if (host->data->flags & MMC_DATA_READ)
@@ -308,7 +284,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
else
sdhci_write_block_pio(host);
if (host->num_sg == 0)
host->blocks--;
if (host->blocks == 0)
break;
}
@@ -360,7 +337,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
host->align_addr = dma_map_single(mmc_dev(host->mmc),
host->align_buffer, 128 * 4, direction);
if (dma_mapping_error(host->align_addr))
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto fail;
BUG_ON(host->align_addr & 0x3);
@@ -389,6 +366,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
if (offset) {
if (data->flags & MMC_DATA_WRITE) {
buffer = sdhci_kmap_atomic(sg, &flags);
WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
memcpy(align, buffer, offset);
sdhci_kunmap_atomic(buffer, &flags);
}
@@ -461,7 +439,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
host->adma_addr = dma_map_single(mmc_dev(host->mmc),
host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
if (dma_mapping_error(host->align_addr))
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto unmap_entries;
BUG_ON(host->adma_addr & 0x3);
@@ -510,6 +488,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
size = 4 - (sg_dma_address(sg) & 0x3);
buffer = sdhci_kmap_atomic(sg, &flags);
WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
memcpy(buffer, align, size);
sdhci_kunmap_atomic(buffer, &flags);
@@ -687,7 +666,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
WARN_ON(1);
host->flags &= ~SDHCI_USE_DMA;
} else {
WARN_ON(count != 1);
WARN_ON(sg_cnt != 1);
writel(sg_dma_address(data->sg),
host->ioaddr + SDHCI_DMA_ADDRESS);
}
@@ -711,11 +690,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
}
if (!(host->flags & SDHCI_REQ_USE_DMA)) {
host->cur_sg = data->sg;
host->num_sg = data->sg_len;
host->offset = 0;
host->remain = host->cur_sg->length;
sg_miter_start(&host->sg_miter,
data->sg, data->sg_len, SG_MITER_ATOMIC);
host->blocks = data->blocks;
}
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
@@ -1581,9 +1558,15 @@ int sdhci_add_host(struct sdhci_host *host)
}
}
/* XXX: Hack to get MMC layer to avoid highmem */
if (!(host->flags & SDHCI_USE_DMA))
mmc_dev(host->mmc)->dma_mask = NULL;
/*
* If we use DMA, then it's up to the caller to set the DMA
* mask, but PIO does not need the hw shim so we set a new
* mask here in that case.
*/
if (!(host->flags & SDHCI_USE_DMA)) {
host->dma_mask = DMA_BIT_MASK(64);
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
}
host->max_clk =
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+5 -4
View File
@@ -9,6 +9,8 @@
* your option) any later version.
*/
#include <linux/scatterlist.h>
/*
* Controller registers
*/
@@ -212,6 +214,7 @@ struct sdhci_host {
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
u64 dma_mask; /* custom DMA mask */
#ifdef CONFIG_LEDS_CLASS
struct led_classdev led; /* LED control */
@@ -238,10 +241,8 @@ struct sdhci_host {
struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */
struct scatterlist *cur_sg; /* We're working on this */
int num_sg; /* Entries left */
int offset; /* Offset into current sg */
int remain; /* Bytes left in current */
struct sg_mapping_iter sg_miter; /* SG state for PIO */
unsigned int blocks; /* remaining PIO blocks */
int sg_count; /* Mapped sg entries */