You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge commit 'upstream/master'
This commit is contained in:
+221
-4
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user