Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (68 commits)
  sdio_uart: Fix SDIO break control to now return success or an error
  mmc: host driver for Ricoh Bay1Controllers
  sdio: sdio_io.c Fix sparse warnings
  sdio: fix the use of hard coded timeout value.
  mmc: OLPC: update vdd/powerup quirk comment
  mmc: fix spares errors of sdhci.c
  mmc: remove multiwrite capability
  wbsd: fix bad dma_addr_t conversion
  atmel-mci: Driver for Atmel on-chip MMC controllers
  mmc: fix sdio_io sparse errors
  mmc: wbsd.c fix shadowing of 'dma' variable
  MMC: S3C24XX: Refuse incorrectly aligned transfers
  MMC: S3C24XX: Add maintainer entry
  MMC: S3C24XX: Update error debugging.
  MMC: S3C24XX: Add media presence test to request handling.
  MMC: S3C24XX: Fix use of msecs where jiffies are needed
  MMC: S3C24XX: Add MODULE_ALIAS() entries for the platform devices
  MMC: S3C24XX: Fix s3c2410_dma_request() return code check.
  MMC: S3C24XX: Allow card-detect on non-IRQ capable pin
  MMC: S3C24XX: Ensure host->mrq->data is valid
  ...

Manually fixed up bogus executable bits on drivers/mmc/core/sdio_io.c
and include/linux/mmc/sdio_func.h when merging.
This commit is contained in:
Linus Torvalds 2008-07-16 15:11:07 -07:00
commit 8a0ca91e1d
45 changed files with 6086 additions and 1352 deletions

View File

@ -348,7 +348,9 @@ W: http://www.linux-usb.org/SpeedTouch/
S: Maintained
ALCHEMY AU1XX0 MMC DRIVER
S: Orphan
P: Manuel Lauss
M: manuel.lauss@gmail.com
S: Maintained
ALI1563 I2C DRIVER
P: Rudolf Marek
@ -3559,6 +3561,13 @@ L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported
S3C24XX SD/MMC Driver
P: Ben Dooks
M: ben-linux@fluff.org
L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
L: linux-kernel@vger.kernel.org
S: Supported
SAA7146 VIDEO4LINUX-2 DRIVER
P: Michael Hunold
M: michael@mihu.de
@ -3631,6 +3640,12 @@ P: Jim Cromie
M: jim.cromie@gmail.com
S: Maintained
SDRICOH_CS MMC/SD HOST CONTROLLER INTERFACE DRIVER
P: Sascha Sommer
M: saschasommer@freenet.de
L: sdricohcs-devel@lists.sourceforge.net (subscribers-only)
S: Maintained
SECURITY CONTACT
P: Security Officers
M: security@kernel.org

View File

@ -19,6 +19,7 @@
#include <linux/leds.h>
#include <linux/spi/spi.h>
#include <asm/atmel-mci.h>
#include <asm/io.h>
#include <asm/setup.h>
@ -51,6 +52,11 @@ static struct spi_board_info spi0_board_info[] __initdata = {
},
};
static struct mci_platform_data __initdata mci0_data = {
.detect_pin = GPIO_PIN_PC(25),
.wp_pin = GPIO_PIN_PE(0),
};
/*
* The next two functions should go away as the boot loader is
* supposed to initialize the macb address registers with a valid
@ -170,6 +176,7 @@ static int __init atngw100_init(void)
set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info));
at32_add_device_mci(0, &mci0_data);
at32_add_device_usba(0, NULL);
for (i = 0; i < ARRAY_SIZE(ngw_leds); i++) {

View File

@ -234,6 +234,9 @@ static int __init atstk1002_init(void)
#ifdef CONFIG_BOARD_ATSTK100X_SPI1
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
#endif
#ifndef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM
at32_add_device_mci(0, NULL);
#endif
#ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM
set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
#else

View File

@ -14,6 +14,7 @@
#include <linux/spi/spi.h>
#include <linux/usb/atmel_usba_udc.h>
#include <asm/atmel-mci.h>
#include <asm/io.h>
#include <asm/irq.h>
@ -1278,20 +1279,32 @@ static struct clk atmel_mci0_pclk = {
.index = 9,
};
struct platform_device *__init at32_add_device_mci(unsigned int id)
struct platform_device *__init
at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
{
struct platform_device *pdev;
struct mci_platform_data _data;
struct platform_device *pdev;
struct dw_dma_slave *dws;
if (id != 0)
return NULL;
pdev = platform_device_alloc("atmel_mci", id);
if (!pdev)
return NULL;
goto fail;
if (platform_device_add_resources(pdev, atmel_mci0_resource,
ARRAY_SIZE(atmel_mci0_resource)))
goto err_add_resources;
goto fail;
if (!data) {
data = &_data;
memset(data, 0, sizeof(struct mci_platform_data));
}
if (platform_device_add_data(pdev, data,
sizeof(struct mci_platform_data)))
goto fail;
select_peripheral(PA(10), PERIPH_A, 0); /* CLK */
select_peripheral(PA(11), PERIPH_A, 0); /* CMD */
@ -1300,12 +1313,19 @@ struct platform_device *__init at32_add_device_mci(unsigned int id)
select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */
select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */
if (data) {
if (data->detect_pin != GPIO_PIN_NONE)
at32_select_gpio(data->detect_pin, 0);
if (data->wp_pin != GPIO_PIN_NONE)
at32_select_gpio(data->wp_pin, 0);
}
atmel_mci0_pclk.dev = &pdev->dev;
platform_device_add(pdev);
return pdev;
err_add_resources:
fail:
platform_device_put(pdev);
return NULL;
}

View File

@ -2,7 +2,7 @@
* Block driver for media (i.e., flash cards)
*
* Copyright 2002 Hewlett-Packard Company
* Copyright 2005-2007 Pierre Ossman
* Copyright 2005-2008 Pierre Ossman
*
* Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
@ -237,17 +237,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
if (brq.data.blocks > card->host->max_blk_count)
brq.data.blocks = card->host->max_blk_count;
/*
* If the host doesn't support multiple block writes, force
* block writes to single block. SD cards are excepted from
* this rule as they support querying the number of
* successfully written sectors.
*/
if (rq_data_dir(req) != READ &&
!(card->host->caps & MMC_CAP_MULTIWRITE) &&
!mmc_card_sd(card))
brq.data.blocks = 1;
if (brq.data.blocks > 1) {
/* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
@ -296,22 +285,24 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
mmc_queue_bounce_post(mq);
/*
* Check for errors here, but don't jump to cmd_err
* until later as we need to wait for the card to leave
* programming mode even when things go wrong.
*/
if (brq.cmd.error) {
printk(KERN_ERR "%s: error %d sending read/write command\n",
req->rq_disk->disk_name, brq.cmd.error);
goto cmd_err;
}
if (brq.data.error) {
printk(KERN_ERR "%s: error %d transferring data\n",
req->rq_disk->disk_name, brq.data.error);
goto cmd_err;
}
if (brq.stop.error) {
printk(KERN_ERR "%s: error %d sending stop command\n",
req->rq_disk->disk_name, brq.stop.error);
goto cmd_err;
}
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
@ -344,6 +335,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
#endif
}
if (brq.cmd.error || brq.data.error || brq.stop.error)
goto cmd_err;
/*
* A block was successfully transferred.
*/
@ -362,30 +356,32 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
* mark the known good sectors as ok.
*
* If the card is not SD, we can still ok written sectors
* if the controller can do proper error reporting.
* as reported by the controller (which might be less than
* the real number of written sectors, but never more).
*
* For reads we just fail the entire chunk as that should
* be safe in all cases.
*/
if (rq_data_dir(req) != READ && mmc_card_sd(card)) {
u32 blocks;
unsigned int bytes;
if (rq_data_dir(req) != READ) {
if (mmc_card_sd(card)) {
u32 blocks;
unsigned int bytes;
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
if (card->csd.write_partial)
bytes = blocks << md->block_bits;
else
bytes = blocks << 9;
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
if (card->csd.write_partial)
bytes = blocks << md->block_bits;
else
bytes = blocks << 9;
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, bytes);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, bytes);
ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
} else if (rq_data_dir(req) != READ &&
(card->host->caps & MMC_CAP_MULTIWRITE)) {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
mmc_release_host(card->host);

View File

@ -1,7 +1,7 @@
/*
* linux/drivers/mmc/card/mmc_test.c
*
* Copyright 2007 Pierre Ossman
* Copyright 2007-2008 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,13 +26,17 @@
struct mmc_test_card {
struct mmc_card *card;
u8 scratch[BUFFER_SIZE];
u8 *buffer;
};
/*******************************************************************/
/* Helper functions */
/* General helper functions */
/*******************************************************************/
/*
* Configure correct block size in card
*/
static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
{
struct mmc_command cmd;
@ -48,117 +52,61 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
return 0;
}
static int __mmc_test_transfer(struct mmc_test_card *test, int write,
unsigned broken_xfer, u8 *buffer, unsigned addr,
unsigned blocks, unsigned blksz)
/*
* Fill in the mmc_request structure given a set of transfer parameters.
*/
static void mmc_test_prepare_mrq(struct mmc_test_card *test,
struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
{
BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
if (blocks > 1) {
mrq->cmd->opcode = write ?
MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
} else {
mrq->cmd->opcode = write ?
MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
}
mrq->cmd->arg = dev_addr;
mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
if (blocks == 1)
mrq->stop = NULL;
else {
mrq->stop->opcode = MMC_STOP_TRANSMISSION;
mrq->stop->arg = 0;
mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
}
mrq->data->blksz = blksz;
mrq->data->blocks = blocks;
mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
mrq->data->sg = sg;
mrq->data->sg_len = sg_len;
mmc_set_data_timeout(mrq->data, test->card);
}
/*
* Wait for the card to finish the busy state
*/
static int mmc_test_wait_busy(struct mmc_test_card *test)
{
int ret, busy;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
struct scatterlist sg;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
memset(&cmd, 0, sizeof(struct mmc_command));
if (broken_xfer) {
if (blocks > 1) {
cmd.opcode = write ?
MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
} else {
cmd.opcode = MMC_SEND_STATUS;
}
} else {
if (blocks > 1) {
cmd.opcode = write ?
MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
} else {
cmd.opcode = write ?
MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
}
}
if (broken_xfer && blocks == 1)
cmd.arg = test->card->rca << 16;
else
cmd.arg = addr;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&stop, 0, sizeof(struct mmc_command));
if (!broken_xfer && (blocks > 1)) {
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
mrq.stop = &stop;
}
memset(&data, 0, sizeof(struct mmc_data));
data.blksz = blksz;
data.blocks = blocks;
data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, buffer, blocks * blksz);
mmc_set_data_timeout(&data, test->card);
mmc_wait_for_req(test->card->host, &mrq);
ret = 0;
if (broken_xfer) {
if (!ret && cmd.error)
ret = cmd.error;
if (!ret && data.error == 0)
ret = RESULT_FAIL;
if (!ret && data.error != -ETIMEDOUT)
ret = data.error;
if (!ret && stop.error)
ret = stop.error;
if (blocks > 1) {
if (!ret && data.bytes_xfered > blksz)
ret = RESULT_FAIL;
} else {
if (!ret && data.bytes_xfered > 0)
ret = RESULT_FAIL;
}
} else {
if (!ret && cmd.error)
ret = cmd.error;
if (!ret && data.error)
ret = data.error;
if (!ret && stop.error)
ret = stop.error;
if (!ret && data.bytes_xfered != blocks * blksz)
ret = RESULT_FAIL;
}
if (ret == -EINVAL)
ret = RESULT_UNSUP_HOST;
busy = 0;
do {
int ret2;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = test->card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
ret2 = mmc_wait_for_cmd(test->card->host, &cmd, 0);
if (ret2)
ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
if (ret)
break;
if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
@ -172,14 +120,57 @@ static int __mmc_test_transfer(struct mmc_test_card *test, int write,
return ret;
}
static int mmc_test_transfer(struct mmc_test_card *test, int write,
u8 *buffer, unsigned addr, unsigned blocks, unsigned blksz)
/*
* Transfer a single sector of kernel addressable data
*/
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
u8 *buffer, unsigned addr, unsigned blksz, int write)
{
return __mmc_test_transfer(test, write, 0, buffer,
addr, blocks, blksz);
int ret;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
struct scatterlist sg;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
memset(&stop, 0, sizeof(struct mmc_command));
mrq.cmd = &cmd;
mrq.data = &data;
mrq.stop = &stop;
sg_init_one(&sg, buffer, blksz);
mmc_test_prepare_mrq(test, &mrq, &sg, 1, addr, 1, blksz, write);
mmc_wait_for_req(test->card->host, &mrq);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
ret = mmc_test_wait_busy(test);
if (ret)
return ret;
return 0;
}
static int mmc_test_prepare_verify(struct mmc_test_card *test, int write)
/*******************************************************************/
/* Test preparation and cleanup */
/*******************************************************************/
/*
* Fill the first couple of sectors of the card with known data
* so that bad reads/writes can be detected
*/
static int __mmc_test_prepare(struct mmc_test_card *test, int write)
{
int ret, i;
@ -188,15 +179,14 @@ static int mmc_test_prepare_verify(struct mmc_test_card *test, int write)
return ret;
if (write)
memset(test->buffer, 0xDF, BUFFER_SIZE);
memset(test->buffer, 0xDF, 512);
else {
for (i = 0;i < BUFFER_SIZE;i++)
for (i = 0;i < 512;i++)
test->buffer[i] = i;
}
for (i = 0;i < BUFFER_SIZE / 512;i++) {
ret = mmc_test_transfer(test, 1, test->buffer + i * 512,
i * 512, 1, 512);
ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
if (ret)
return ret;
}
@ -204,41 +194,218 @@ static int mmc_test_prepare_verify(struct mmc_test_card *test, int write)
return 0;
}
static int mmc_test_prepare_verify_write(struct mmc_test_card *test)
static int mmc_test_prepare_write(struct mmc_test_card *test)
{
return mmc_test_prepare_verify(test, 1);
return __mmc_test_prepare(test, 1);
}
static int mmc_test_prepare_verify_read(struct mmc_test_card *test)
static int mmc_test_prepare_read(struct mmc_test_card *test)
{
return mmc_test_prepare_verify(test, 0);
return __mmc_test_prepare(test, 0);
}
static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,
u8 *buffer, unsigned addr, unsigned blocks, unsigned blksz)
static int mmc_test_cleanup(struct mmc_test_card *test)
{
int ret, i, sectors;
int ret, i;
/*
* It is assumed that the above preparation has been done.
*/
ret = mmc_test_set_blksize(test, 512);
if (ret)
return ret;
memset(test->buffer, 0, BUFFER_SIZE);
memset(test->buffer, 0, 512);
for (i = 0;i < BUFFER_SIZE / 512;i++) {
ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
if (ret)
return ret;
}
return 0;
}
/*******************************************************************/
/* Test execution helpers */
/*******************************************************************/
/*
* Modifies the mmc_request to perform the "short transfer" tests
*/
static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
struct mmc_request *mrq, int write)
{
BUG_ON(!mrq || !mrq->cmd || !mrq->data);
if (mrq->data->blocks > 1) {
mrq->cmd->opcode = write ?
MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
mrq->stop = NULL;
} else {
mrq->cmd->opcode = MMC_SEND_STATUS;
mrq->cmd->arg = test->card->rca << 16;
}
}
/*
* Checks that a normal transfer didn't have any errors
*/
static int mmc_test_check_result(struct mmc_test_card *test,
struct mmc_request *mrq)
{
int ret;
BUG_ON(!mrq || !mrq->cmd || !mrq->data);
ret = 0;
if (!ret && mrq->cmd->error)
ret = mrq->cmd->error;
if (!ret && mrq->data->error)
ret = mrq->data->error;
if (!ret && mrq->stop && mrq->stop->error)
ret = mrq->stop->error;
if (!ret && mrq->data->bytes_xfered !=
mrq->data->blocks * mrq->data->blksz)
ret = RESULT_FAIL;
if (ret == -EINVAL)
ret = RESULT_UNSUP_HOST;
return ret;
}
/*
* Checks that a "short transfer" behaved as expected
*/
static int mmc_test_check_broken_result(struct mmc_test_card *test,
struct mmc_request *mrq)
{
int ret;
BUG_ON(!mrq || !mrq->cmd || !mrq->data);
ret = 0;
if (!ret && mrq->cmd->error)
ret = mrq->cmd->error;
if (!ret && mrq->data->error == 0)
ret = RESULT_FAIL;
if (!ret && mrq->data->error != -ETIMEDOUT)
ret = mrq->data->error;
if (!ret && mrq->stop && mrq->stop->error)
ret = mrq->stop->error;
if (mrq->data->blocks > 1) {
if (!ret && mrq->data->bytes_xfered > mrq->data->blksz)
ret = RESULT_FAIL;
} else {
if (!ret && mrq->data->bytes_xfered > 0)
ret = RESULT_FAIL;
}
if (ret == -EINVAL)
ret = RESULT_UNSUP_HOST;
return ret;
}
/*
* Tests a basic transfer with certain parameters
*/
static int mmc_test_simple_transfer(struct mmc_test_card *test,
struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
unsigned blocks, unsigned blksz, int write)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
memset(&stop, 0, sizeof(struct mmc_command));
mrq.cmd = &cmd;
mrq.data = &data;
mrq.stop = &stop;
mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
blocks, blksz, write);
mmc_wait_for_req(test->card->host, &mrq);
mmc_test_wait_busy(test);
return mmc_test_check_result(test, &mrq);
}
/*
* Tests a transfer where the card will fail completely or partly
*/
static int mmc_test_broken_transfer(struct mmc_test_card *test,
unsigned blocks, unsigned blksz, int write)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
struct scatterlist sg;
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
memset(&stop, 0, sizeof(struct mmc_command));
mrq.cmd = &cmd;
mrq.data = &data;
mrq.stop = &stop;
sg_init_one(&sg, test->buffer, blocks * blksz);
mmc_test_prepare_mrq(test, &mrq, &sg, 1, 0, blocks, blksz, write);
mmc_test_prepare_broken_mrq(test, &mrq, write);
mmc_wait_for_req(test->card->host, &mrq);
mmc_test_wait_busy(test);
return mmc_test_check_broken_result(test, &mrq);
}
/*
* Does a complete transfer test where data is also validated
*
* Note: mmc_test_prepare() must have been done before this call
*/
static int mmc_test_transfer(struct mmc_test_card *test,
struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
unsigned blocks, unsigned blksz, int write)
{
int ret, i;
unsigned long flags;
if (write) {
for (i = 0;i < blocks * blksz;i++)
buffer[i] = i;
test->scratch[i] = i;
} else {
memset(test->scratch, 0, BUFFER_SIZE);
}
local_irq_save(flags);
sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
local_irq_restore(flags);
ret = mmc_test_set_blksize(test, blksz);
if (ret)
return ret;
ret = mmc_test_transfer(test, write, buffer, addr, blocks, blksz);
ret = mmc_test_simple_transfer(test, sg, sg_len, dev_addr,
blocks, blksz, write);
if (ret)
return ret;
if (write) {
int sectors;
ret = mmc_test_set_blksize(test, 512);
if (ret)
return ret;
@ -253,9 +420,9 @@ static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,
memset(test->buffer, 0, sectors * 512);
for (i = 0;i < sectors;i++) {
ret = mmc_test_transfer(test, 0,
ret = mmc_test_buffer_transfer(test,
test->buffer + i * 512,
addr + i * 512, 1, 512);
dev_addr + i * 512, 512, 0);
if (ret)
return ret;
}
@ -270,8 +437,11 @@ static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,
return RESULT_FAIL;
}
} else {
local_irq_save(flags);
sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
local_irq_restore(flags);
for (i = 0;i < blocks * blksz;i++) {
if (buffer[i] != (u8)i)
if (test->scratch[i] != (u8)i)
return RESULT_FAIL;
}
}
@ -279,26 +449,6 @@ static int mmc_test_verified_transfer(struct mmc_test_card *test, int write,
return 0;
}
static int mmc_test_cleanup_verify(struct mmc_test_card *test)
{
int ret, i;
ret = mmc_test_set_blksize(test, 512);
if (ret)
return ret;
memset(test->buffer, 0, BUFFER_SIZE);
for (i = 0;i < BUFFER_SIZE / 512;i++) {
ret = mmc_test_transfer(test, 1, test->buffer + i * 512,
i * 512, 1, 512);
if (ret)
return ret;
}
return 0;
}
/*******************************************************************/
/* Tests */
/*******************************************************************/
@ -314,12 +464,15 @@ struct mmc_test_case {
static int mmc_test_basic_write(struct mmc_test_card *test)
{
int ret;
struct scatterlist sg;
ret = mmc_test_set_blksize(test, 512);
if (ret)
return ret;
ret = mmc_test_transfer(test, 1, test->buffer, 0, 1, 512);
sg_init_one(&sg, test->buffer, 512);
ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
if (ret)
return ret;
@ -329,12 +482,15 @@ static int mmc_test_basic_write(struct mmc_test_card *test)
static int mmc_test_basic_read(struct mmc_test_card *test)
{
int ret;
struct scatterlist sg;
ret = mmc_test_set_blksize(test, 512);
if (ret)
return ret;
ret = mmc_test_transfer(test, 0, test->buffer, 0, 1, 512);
sg_init_one(&sg, test->buffer, 512);
ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
if (ret)
return ret;
@ -344,8 +500,11 @@ static int mmc_test_basic_read(struct mmc_test_card *test)
static int mmc_test_verify_write(struct mmc_test_card *test)
{
int ret;
struct scatterlist sg;
ret = mmc_test_verified_transfer(test, 1, test->buffer, 0, 1, 512);
sg_init_one(&sg, test->buffer, 512);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
if (ret)
return ret;
@ -355,8 +514,11 @@ static int mmc_test_verify_write(struct mmc_test_card *test)
static int mmc_test_verify_read(struct mmc_test_card *test)
{
int ret;
struct scatterlist sg;
ret = mmc_test_verified_transfer(test, 0, test->buffer, 0, 1, 512);
sg_init_one(&sg, test->buffer, 512);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
if (ret)
return ret;
@ -367,6 +529,7 @@ static int mmc_test_multi_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;
@ -379,8 +542,9 @@ static int mmc_test_multi_write(struct mmc_test_card *test)
if (size < 1024)
return RESULT_UNSUP_HOST;
ret = mmc_test_verified_transfer(test, 1, test->buffer, 0,
size / 512, 512);
sg_init_one(&sg, test->buffer, size);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
if (ret)
return ret;
@ -391,6 +555,7 @@ static int mmc_test_multi_read(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;
@ -403,8 +568,9 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
if (size < 1024)
return RESULT_UNSUP_HOST;
ret = mmc_test_verified_transfer(test, 0, test->buffer, 0,
size / 512, 512);
sg_init_one(&sg, test->buffer, size);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
if (ret)
return ret;
@ -414,13 +580,14 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
static int mmc_test_pow2_write(struct mmc_test_card *test)
{
int ret, i;
struct scatterlist sg;
if (!test->card->csd.write_partial)
return RESULT_UNSUP_CARD;
for (i = 1; i < 512;i <<= 1) {
ret = mmc_test_verified_transfer(test, 1,
test->buffer, 0, 1, i);
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
if (ret)
return ret;
}
@ -431,13 +598,14 @@ static int mmc_test_pow2_write(struct mmc_test_card *test)
static int mmc_test_pow2_read(struct mmc_test_card *test)
{
int ret, i;
struct scatterlist sg;
if (!test->card->csd.read_partial)
return RESULT_UNSUP_CARD;
for (i = 1; i < 512;i <<= 1) {
ret = mmc_test_verified_transfer(test, 0,
test->buffer, 0, 1, i);
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
if (ret)
return ret;
}
@ -448,13 +616,14 @@ static int mmc_test_pow2_read(struct mmc_test_card *test)
static int mmc_test_weird_write(struct mmc_test_card *test)
{
int ret, i;
struct scatterlist sg;
if (!test->card->csd.write_partial)
return RESULT_UNSUP_CARD;
for (i = 3; i < 512;i += 7) {
ret = mmc_test_verified_transfer(test, 1,
test->buffer, 0, 1, i);
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
if (ret)
return ret;
}
@ -465,13 +634,14 @@ static int mmc_test_weird_write(struct mmc_test_card *test)
static int mmc_test_weird_read(struct mmc_test_card *test)
{
int ret, i;
struct scatterlist sg;
if (!test->card->csd.read_partial)
return RESULT_UNSUP_CARD;
for (i = 3; i < 512;i += 7) {
ret = mmc_test_verified_transfer(test, 0,
test->buffer, 0, 1, i);
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
if (ret)
return ret;
}
@ -482,10 +652,11 @@ static int mmc_test_weird_read(struct mmc_test_card *test)
static int mmc_test_align_write(struct mmc_test_card *test)
{
int ret, i;
struct scatterlist sg;
for (i = 1;i < 4;i++) {
ret = mmc_test_verified_transfer(test, 1, test->buffer + i,
0, 1, 512);
sg_init_one(&sg, test->buffer + i, 512);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
if (ret)
return ret;
}
@ -496,10 +667,11 @@ static int mmc_test_align_write(struct mmc_test_card *test)
static int mmc_test_align_read(struct mmc_test_card *test)
{
int ret, i;
struct scatterlist sg;
for (i = 1;i < 4;i++) {
ret = mmc_test_verified_transfer(test, 0, test->buffer + i,
0, 1, 512);
sg_init_one(&sg, test->buffer + i, 512);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
if (ret)
return ret;
}
@ -511,6 +683,7 @@ static int mmc_test_align_multi_write(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;
@ -524,8 +697,8 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test)
return RESULT_UNSUP_HOST;
for (i = 1;i < 4;i++) {
ret = mmc_test_verified_transfer(test, 1, test->buffer + i,
0, size / 512, 512);
sg_init_one(&sg, test->buffer + i, size);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
if (ret)
return ret;
}
@ -537,6 +710,7 @@ static int mmc_test_align_multi_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;
@ -550,8 +724,8 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)
return RESULT_UNSUP_HOST;
for (i = 1;i < 4;i++) {
ret = mmc_test_verified_transfer(test, 0, test->buffer + i,
0, size / 512, 512);
sg_init_one(&sg, test->buffer + i, size);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
if (ret)
return ret;
}
@ -567,7 +741,7 @@ static int mmc_test_xfersize_write(struct mmc_test_card *test)
if (ret)
return ret;
ret = __mmc_test_transfer(test, 1, 1, test->buffer, 0, 1, 512);
ret = mmc_test_broken_transfer(test, 1, 512, 1);
if (ret)
return ret;
@ -582,7 +756,7 @@ static int mmc_test_xfersize_read(struct mmc_test_card *test)
if (ret)
return ret;
ret = __mmc_test_transfer(test, 0, 1, test->buffer, 0, 1, 512);
ret = mmc_test_broken_transfer(test, 1, 512, 0);
if (ret)
return ret;
@ -600,7 +774,7 @@ static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
if (ret)
return ret;
ret = __mmc_test_transfer(test, 1, 1, test->buffer, 0, 2, 512);
ret = mmc_test_broken_transfer(test, 2, 512, 1);
if (ret)
return ret;
@ -618,7 +792,7 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
if (ret)
return ret;
ret = __mmc_test_transfer(test, 0, 1, test->buffer, 0, 2, 512);
ret = mmc_test_broken_transfer(test, 2, 512, 0);
if (ret)
return ret;
@ -638,86 +812,86 @@ static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (with data verification)",
.prepare = mmc_test_prepare_verify_write,
.prepare = mmc_test_prepare_write,
.run = mmc_test_verify_write,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Basic read (with data verification)",
.prepare = mmc_test_prepare_verify_read,
.prepare = mmc_test_prepare_read,
.run = mmc_test_verify_read,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Multi-block write",
.prepare = mmc_test_prepare_verify_write,
.prepare = mmc_test_prepare_write,
.run = mmc_test_multi_write,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Multi-block read",
.prepare = mmc_test_prepare_verify_read,
.prepare = mmc_test_prepare_read,
.run = mmc_test_multi_read,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Power of two block writes",
.prepare = mmc_test_prepare_verify_write,
.prepare = mmc_test_prepare_write,
.run = mmc_test_pow2_write,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Power of two block reads",
.prepare = mmc_test_prepare_verify_read,
.prepare = mmc_test_prepare_read,
.run = mmc_test_pow2_read,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Weird sized block writes",
.prepare = mmc_test_prepare_verify_write,
.prepare = mmc_test_prepare_write,
.run = mmc_test_weird_write,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Weird sized block reads",
.prepare = mmc_test_prepare_verify_read,
.prepare = mmc_test_prepare_read,
.run = mmc_test_weird_read,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Badly aligned write",
.prepare = mmc_test_prepare_verify_write,
.prepare = mmc_test_prepare_write,
.run = mmc_test_align_write,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Badly aligned read",
.prepare = mmc_test_prepare_verify_read,
.prepare = mmc_test_prepare_read,
.run = mmc_test_align_read,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Badly aligned multi-block write",
.prepare = mmc_test_prepare_verify_write,
.prepare = mmc_test_prepare_write,
.run = mmc_test_align_multi_write,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
.name = "Badly aligned multi-block read",
.prepare = mmc_test_prepare_verify_read,
.prepare = mmc_test_prepare_read,
.run = mmc_test_align_multi_read,
.cleanup = mmc_test_cleanup_verify,
.cleanup = mmc_test_cleanup,
},
{
@ -743,7 +917,7 @@ static const struct mmc_test_case mmc_test_cases[] = {
static struct mutex mmc_test_lock;
static void mmc_test_run(struct mmc_test_card *test)
static void mmc_test_run(struct mmc_test_card *test, int testcase)
{
int i, ret;
@ -753,6 +927,9 @@ static void mmc_test_run(struct mmc_test_card *test)
mmc_claim_host(test->card->host);
for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
if (testcase && ((i + 1) != testcase))
continue;
printk(KERN_INFO "%s: Test case %d. %s...\n",
mmc_hostname(test->card->host), i + 1,
mmc_test_cases[i].name);
@ -824,9 +1001,12 @@ static ssize_t mmc_test_store(struct device *dev,
{
struct mmc_card *card;
struct mmc_test_card *test;
int testcase;
card = container_of(dev, struct mmc_card, dev);
testcase = simple_strtol(buf, NULL, 10);
test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
if (!test)
return -ENOMEM;
@ -836,7 +1016,7 @@ static ssize_t mmc_test_store(struct device *dev,
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
if (test->buffer) {
mutex_lock(&mmc_test_lock);
mmc_test_run(test);
mmc_test_run(test, testcase);
mutex_unlock(&mmc_test_lock);
}
@ -852,6 +1032,9 @@ static int mmc_test_probe(struct mmc_card *card)
{
int ret;
if ((card->type != MMC_TYPE_MMC) && (card->type != MMC_TYPE_SD))
return -ENODEV;
mutex_init(&mmc_test_lock);
ret = device_create_file(&card->dev, &dev_attr_test);

View File

@ -885,12 +885,14 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t
sdio_uart_release_func(port);
}
static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
{
struct sdio_uart_port *port = tty->driver_data;
int result;
if (sdio_uart_claim_func(port) != 0)
return;
result = sdio_uart_claim_func(port);
if (result != 0)
return result;
if (break_state == -1)
port->lcr |= UART_LCR_SBC;
@ -899,6 +901,7 @@ static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
sdio_out(port, UART_LCR, port->lcr);
sdio_uart_release_func(port);
return 0;
}
static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
@ -294,6 +294,33 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
}
EXPORT_SYMBOL(mmc_set_data_timeout);
/**
* mmc_align_data_size - pads a transfer size to a more optimal value
* @card: the MMC card associated with the data transfer
* @sz: original transfer size
*
* Pads the original data size with a number of extra bytes in
* order to avoid controller bugs and/or performance hits
* (e.g. some controllers revert to PIO for certain sizes).
*
* Returns the improved size, which might be unmodified.
*
* Note that this function is only relevant when issuing a
* single scatter gather entry.
*/
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
{
/*
* FIXME: We don't have a system for the controller to tell
* the core about its problems yet, so for now we just 32-bit
* align the size.
*/
sz = ((sz + 3) / 4) * 4;
return sz;
}
EXPORT_SYMBOL(mmc_align_data_size);
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
@ -638,6 +665,9 @@ void mmc_rescan(struct work_struct *work)
*/
mmc_bus_put(host);
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out;
mmc_claim_host(host);
mmc_power_up(host);
@ -652,7 +682,7 @@ void mmc_rescan(struct work_struct *work)
if (!err) {
if (mmc_attach_sdio(host, ocr))
mmc_power_off(host);
return;
goto out;
}
/*
@ -662,7 +692,7 @@ void mmc_rescan(struct work_struct *work)
if (!err) {
if (mmc_attach_sd(host, ocr))
mmc_power_off(host);
return;
goto out;
}
/*
@ -672,7 +702,7 @@ void mmc_rescan(struct work_struct *work)
if (!err) {
if (mmc_attach_mmc(host, ocr))
mmc_power_off(host);
return;
goto out;
}
mmc_release_host(host);
@ -683,6 +713,9 @@ void mmc_rescan(struct work_struct *work)
mmc_bus_put(host);
}
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
void mmc_start_host(struct mmc_host *host)

View File

@ -288,7 +288,7 @@ static struct device_type mmc_type = {
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "curcard" will contain the card
* In the case of a resume, "oldcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_init_card(struct mmc_host *host, u32 ocr,

View File

@ -326,7 +326,7 @@ static struct device_type sd_type = {
/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "curcard" will contain the card
* In the case of a resume, "oldcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
@ -494,13 +494,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* Check if read-only switch is active.
*/
if (!oldcard) {
if (!host->ops->get_ro) {
if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {
printk(KERN_WARNING "%s: host does not "
"support reading read-only "
"switch. assuming write-enable.\n",
mmc_hostname(host));
} else {
if (host->ops->get_ro(host))
if (host->ops->get_ro(host) > 0)
mmc_card_set_readonly(card);
}
}

View File

@ -129,6 +129,12 @@ static int cistpl_funce_func(struct sdio_func *func,
/* TPLFE_MAX_BLK_SIZE */
func->max_blksize = buf[12] | (buf[13] << 8);
/* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */
if (vsn > SDIO_SDIO_REV_1_00)
func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10;
else
func->enable_timeout = jiffies_to_msecs(HZ);
return 0;
}

View File

@ -1,7 +1,7 @@
/*
* linux/drivers/mmc/core/sdio_io.c
*
* Copyright 2007 Pierre Ossman
* Copyright 2007-2008 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -76,11 +76,7 @@ int sdio_enable_func(struct sdio_func *func)
if (ret)
goto err;
/*
* FIXME: This should timeout based on information in the CIS,
* but we don't have card to parse that yet.
*/
timeout = jiffies + HZ;
timeout = jiffies + msecs_to_jiffies(func->enable_timeout);
while (1) {
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);
@ -167,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
return -EINVAL;
if (blksz == 0) {
blksz = min(min(
func->max_blksize,
func->card->host->max_blk_size),
512u);
blksz = min(func->max_blksize, func->card->host->max_blk_size);
blksz = min(blksz, 512u);
}
ret = mmc_io_rw_direct(func->card, 1, 0,
@ -186,9 +180,116 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
func->cur_blksize = blksz;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_set_block_size);
/*
* Calculate the maximum byte mode transfer size
*/
static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
{
unsigned mval = min(func->card->host->max_seg_size,
func->card->host->max_blk_size);
mval = min(mval, func->max_blksize);
return min(mval, 512u); /* maximum size for byte mode */
}
/**
* sdio_align_size - pads a transfer size to a more optimal value
* @func: SDIO function
* @sz: original transfer size
*
* Pads the original data size with a number of extra bytes in
* order to avoid controller bugs and/or performance hits
* (e.g. some controllers revert to PIO for certain sizes).
*
* If possible, it will also adjust the size so that it can be
* handled in just a single request.
*
* Returns the improved size, which might be unmodified.
*/
unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
{
unsigned int orig_sz;
unsigned int blk_sz, byte_sz;
unsigned chunk_sz;
orig_sz = sz;
/*
* Do a first check with the controller, in case it
* wants to increase the size up to a point where it
* might need more than one block.
*/
sz = mmc_align_data_size(func->card, sz);
/*
* If we can still do this with just a byte transfer, then
* we're done.
*/
if (sz <= sdio_max_byte_size(func))
return sz;
if (func->card->cccr.multi_block) {
/*
* Check if the transfer is already block aligned
*/
if ((sz % func->cur_blksize) == 0)
return sz;
/*
* Realign it so that it can be done with one request,
* and recheck if the controller still likes it.
*/
blk_sz = ((sz + func->cur_blksize - 1) /
func->cur_blksize) * func->cur_blksize;
blk_sz = mmc_align_data_size(func->card, blk_sz);
/*
* This value is only good if it is still just
* one request.
*/
if ((blk_sz % func->cur_blksize) == 0)
return blk_sz;
/*
* We failed to do one request, but at least try to
* pad the remainder properly.
*/
byte_sz = mmc_align_data_size(func->card,
sz % func->cur_blksize);
if (byte_sz <= sdio_max_byte_size(func)) {
blk_sz = sz / func->cur_blksize;
return blk_sz * func->cur_blksize + byte_sz;
}
} else {
/*
* We need multiple requests, so first check that the
* controller can handle the chunk size;
*/
chunk_sz = mmc_align_data_size(func->card,
sdio_max_byte_size(func));
if (chunk_sz == sdio_max_byte_size(func)) {
/*
* Fix up the size of the remainder (if any)
*/
byte_sz = orig_sz % chunk_sz;
if (byte_sz) {
byte_sz = mmc_align_data_size(func->card,
byte_sz);
}
return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
}
}
/*
* The controller is simply incapable of transferring the size
* we want in decent manner, so just return the original size.
*/
return orig_sz;
}
EXPORT_SYMBOL_GPL(sdio_align_size);
/* Split an arbitrarily sized data transfer into several
* IO_RW_EXTENDED commands. */
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
@ -199,14 +300,13 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
int ret;
/* Do the bulk of the transfer using block mode (if supported). */
if (func->card->cccr.multi_block) {
if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
/* Blocks per command is limited by host count, host transfer
* size (we only use a single sg entry) and the maximum for
* IO_RW_EXTENDED of 511 blocks. */
max_blocks = min(min(
func->card->host->max_blk_count,
func->card->host->max_seg_size / func->cur_blksize),
511u);
max_blocks = min(func->card->host->max_blk_count,
func->card->host->max_seg_size / func->cur_blksize);
max_blocks = min(max_blocks, 511u);
while (remainder > func->cur_blksize) {
unsigned blocks;
@ -231,11 +331,7 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
/* Write the remainder using byte mode. */
while (remainder > 0) {
size = remainder;
if (size > func->cur_blksize)
size = func->cur_blksize;
if (size > 512)
size = 512; /* maximum size for byte mode */
size = min(remainder, sdio_max_byte_size(func));
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
incr_addr, buf, 1, size);
@ -260,11 +356,10 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
* function. If there is a problem reading the address, 0xff
* is returned and @err_ret will contain the error code.
*/
unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
int *err_ret)
u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
{
int ret;
unsigned char val;
u8 val;
BUG_ON(!func);
@ -293,8 +388,7 @@ EXPORT_SYMBOL_GPL(sdio_readb);
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
int *err_ret)
void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)
{
int ret;
@ -355,7 +449,6 @@ int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
{
return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_readsb);
/**
@ -385,8 +478,7 @@ EXPORT_SYMBOL_GPL(sdio_writesb);
* function. If there is a problem reading the address, 0xffff
* is returned and @err_ret will contain the error code.
*/
unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
int *err_ret)
u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret)
{
int ret;
@ -400,7 +492,7 @@ unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
return 0xFFFF;
}
return le16_to_cpu(*(u16*)func->tmpbuf);
return le16_to_cpup((__le16 *)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readw);
@ -415,12 +507,11 @@ EXPORT_SYMBOL_GPL(sdio_readw);
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
int *err_ret)
void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret)
{
int ret;
*(u16*)func->tmpbuf = cpu_to_le16(b);
*(__le16 *)func->tmpbuf = cpu_to_le16(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
if (err_ret)
@ -439,8 +530,7 @@ EXPORT_SYMBOL_GPL(sdio_writew);
* 0xffffffff is returned and @err_ret will contain the error
* code.
*/
unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
int *err_ret)
u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret)
{
int ret;
@ -454,7 +544,7 @@ unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
return 0xFFFFFFFF;
}
return le32_to_cpu(*(u32*)func->tmpbuf);
return le32_to_cpup((__le32 *)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readl);
@ -469,12 +559,11 @@ EXPORT_SYMBOL_GPL(sdio_readl);
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
int *err_ret)
void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret)
{
int ret;
*(u32*)func->tmpbuf = cpu_to_le32(b);
*(__le32 *)func->tmpbuf = cpu_to_le32(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
if (err_ret)

View File

@ -26,18 +26,31 @@ config MMC_PXA
config MMC_SDHCI
tristate "Secure Digital Host Controller Interface support"
depends on PCI
depends on HAS_DMA
help
This select the generic Secure Digital Host Controller Interface.
This selects the generic Secure Digital Host Controller Interface.
It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
and Toshiba(R). Most controllers found in laptops are of this type.
If you have a controller with this interface, say Y or M here. You
also need to enable an appropriate bus interface.
If unsure, say N.
config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
help
This selects the PCI Secure Digital Host Controller Interface.
Most controllers found today are PCI devices.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_RICOH_MMC
tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL && MMC_SDHCI
depends on MMC_SDHCI_PCI
help
This selects the disabler for the Ricoh MMC Controller. This
proprietary controller is unnecessary because the SDHCI driver
@ -91,6 +104,16 @@ config MMC_AT91
If unsure, say N.
config MMC_ATMELMCI
tristate "Atmel Multimedia Card Interface support"
depends on AVR32
help
This selects the Atmel Multimedia Card Interface driver. If
you have an AT32 (AVR32) platform with a Multimedia Card
slot, say Y or M here.
If unsure, say N.
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
depends on ARCH_IMX
@ -130,3 +153,24 @@ config MMC_SPI
If unsure, or if your system has no SPI master driver, say N.
config MMC_S3C
tristate "Samsung S3C SD/MMC Card Interface support"
depends on ARCH_S3C2410 && MMC
help
This selects a driver for the MCI interface found in
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
If you have a board based on one of those and a MMC/SD
slot, say Y or M here.
If unsure, say N.
config MMC_SDRICOH_CS
tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"
depends on EXPERIMENTAL && MMC && PCI && PCMCIA
help
Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA
card whenever you insert a MMC or SD card into the card slot.
To compile this driver as a module, choose M here: the
module will be called sdricoh_cs.

View File

@ -10,11 +10,15 @@ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
obj-$(CONFIG_MMC_S3C) += s3cmci.o
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o

View File

@ -125,8 +125,71 @@ struct at91mci_host
/* Latest in the scatterlist that has been enabled for transfer */
int transfer_index;
/* Timer for timeouts */
struct timer_list timer;
};
/*
* Reset the controller and restore most of the state
*/
static void at91_reset_host(struct at91mci_host *host)
{
unsigned long flags;
u32 mr;
u32 sdcr;
u32 dtor;
u32 imr;
local_irq_save(flags);
imr = at91_mci_read(host, AT91_MCI_IMR);
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
/* save current state */
mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
sdcr = at91_mci_read(host, AT91_MCI_SDCR);
dtor = at91_mci_read(host, AT91_MCI_DTOR);
/* reset the controller */
at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
/* restore state */
at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
at91_mci_write(host, AT91_MCI_MR, mr);
at91_mci_write(host, AT91_MCI_SDCR, sdcr);
at91_mci_write(host, AT91_MCI_DTOR, dtor);
at91_mci_write(host, AT91_MCI_IER, imr);
/* make sure sdio interrupts will fire */
at91_mci_read(host, AT91_MCI_SR);
local_irq_restore(flags);
}
static void at91_timeout_timer(unsigned long data)
{
struct at91mci_host *host;
host = (struct at91mci_host *)data;
if (host->request) {
dev_err(host->mmc->parent, "Timeout waiting end of packet\n");
if (host->cmd && host->cmd->data) {
host->cmd->data->error = -ETIMEDOUT;
} else {
if (host->cmd)
host->cmd->error = -ETIMEDOUT;
else
host->request->cmd->error = -ETIMEDOUT;
}
at91_reset_host(host);
mmc_request_done(host->mmc, host->request);
}
}
/*
* Copy from sg to a dma block - used for transfers
*/
@ -135,9 +198,14 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
unsigned int len, i, size;
unsigned *dmabuf = host->buffer;
size = host->total_length;
size = data->blksz * data->blocks;
len = data->sg_len;
/* AT91SAM926[0/3] Data Write Operation and number of bytes erratum */
if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
if (host->total_length == 12)
memset(dmabuf, 0, 12);
/*
* Just loop through all entries. Size might not
* be the entire list though so make sure that
@ -159,9 +227,10 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
for (index = 0; index < (amount / 4); index++)
*dmabuf++ = swab32(sgbuffer[index]);
}
else
} else {
memcpy(dmabuf, sgbuffer, amount);
dmabuf += amount;
}
kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
@ -233,11 +302,11 @@ static void at91_mci_pre_dma_read(struct at91mci_host *host)
if (i == 0) {
at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4);
at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
}
else {
at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4);
at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
}
}
@ -277,8 +346,6 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
data->bytes_xfered += sg->length;
if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
unsigned int *buffer;
int index;
@ -294,6 +361,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
}
flush_dcache_page(sg_page(sg));
data->bytes_xfered += sg->length;
}
/* Is there another transfer to trigger? */
@ -334,10 +403,32 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
} else
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
data->bytes_xfered = host->total_length;
}
/*
* Update bytes tranfered count during a write operation
*/
static void at91_mci_update_bytes_xfered(struct at91mci_host *host)
{
struct mmc_data *data;
/* always deal with the effective request (and not the current cmd) */
if (host->request->cmd && host->request->cmd->error != 0)
return;
if (host->request->data) {
data = host->request->data;
if (data->flags & MMC_DATA_WRITE) {
/* card is in IDLE mode now */
pr_debug("-> bytes_xfered %d, total_length = %d\n",
data->bytes_xfered, host->total_length);
data->bytes_xfered = data->blksz * data->blocks;
}
}
}
/*Handle after command sent ready*/
static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
{
@ -350,8 +441,7 @@ static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
} else return 1;
} else if (host->cmd->data->flags & MMC_DATA_WRITE) {
/*After sendding multi-block-write command, start DMA transfer*/
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE);
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE);
at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
}
@ -430,11 +520,19 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
if (data) {
if ( data->blksz & 0x3 ) {
pr_debug("Unsupported block size\n");
cmd->error = -EINVAL;
mmc_request_done(host->mmc, host->request);
return;
if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) {
if (data->blksz & 0x3) {
pr_debug("Unsupported block size\n");
cmd->error = -EINVAL;
mmc_request_done(host->mmc, host->request);
return;
}
if (data->flags & MMC_DATA_STREAM) {
pr_debug("Stream commands not supported\n");
cmd->error = -EINVAL;
mmc_request_done(host->mmc, host->request);
return;
}
}
block_length = data->blksz;
@ -481,8 +579,16 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
ier = AT91_MCI_CMDRDY;
} else {
/* zero block length and PDC mode */
mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff;
mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0;
mr |= (block_length << 16);
mr |= AT91_MCI_PDCMODE;
at91_mci_write(host, AT91_MCI_MR, mr);
if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261()))
at91_mci_write(host, AT91_MCI_BLKR,
AT91_MCI_BLKR_BCNT(blocks) |
AT91_MCI_BLKR_BLKLEN(block_length));
/*
* Disable the PDC controller
@ -508,6 +614,13 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
* Handle a write
*/
host->total_length = block_length * blocks;
/*
* AT91SAM926[0/3] Data Write Operation and
* number of bytes erratum
*/
if (cpu_is_at91sam9260 () || cpu_is_at91sam9263())
if (host->total_length < 12)
host->total_length = 12;
host->buffer = dma_alloc_coherent(NULL,
host->total_length,
&host->physical_address, GFP_KERNEL);
@ -517,7 +630,9 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
pr_debug("Transmitting %d bytes\n", host->total_length);
at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ?
host->total_length : host->total_length / 4);
ier = AT91_MCI_CMDRDY;
}
}
@ -552,20 +667,26 @@ static void at91_mci_process_next(struct at91mci_host *host)
else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
host->flags |= FL_SENT_STOP;
at91_mci_send_command(host, host->request->stop);
}
else
} else {
del_timer(&host->timer);
/* the at91rm9200 mci controller hangs after some transfers,
* and the workaround is to reset it after each transfer.
*/
if (cpu_is_at91rm9200())
at91_reset_host(host);
mmc_request_done(host->mmc, host->request);
}
}
/*
* Handle a command that has been completed
*/
static void at91_mci_completed_command(struct at91mci_host *host)
static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status)
{
struct mmc_command *cmd = host->cmd;
unsigned int status;
struct mmc_data *data = cmd->data;
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
@ -577,25 +698,34 @@ static void at91_mci_completed_command(struct at91mci_host *host)
host->buffer = NULL;
}
status = at91_mci_read(host, AT91_MCI_SR);
pr_debug("Status = %08X [%08X %08X %08X %08X]\n",
status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n",
status, at91_mci_read(host, AT91_MCI_SR),
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
if (status & AT91_MCI_ERRORS) {
if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
cmd->error = 0;
}
else {
if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
cmd->error = -ETIMEDOUT;
else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
cmd->error = -EILSEQ;
else
cmd->error = -EIO;
if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) {
if (data) {
if (status & AT91_MCI_DTOE)
data->error = -ETIMEDOUT;
else if (status & AT91_MCI_DCRCE)
data->error = -EILSEQ;
}
} else {
if (status & AT91_MCI_RTOE)
cmd->error = -ETIMEDOUT;
else if (status & AT91_MCI_RCRCE)
cmd->error = -EILSEQ;
else
cmd->error = -EIO;
}
pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n",
cmd->error, cmd->opcode, cmd->retries);
pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n",
cmd->error, data ? data->error : 0,
cmd->opcode, cmd->retries);
}
}
else
@ -613,6 +743,8 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->request = mrq;
host->flags = 0;
mod_timer(&host->timer, jiffies + HZ);
at91_mci_process_next(host);
}
@ -736,6 +868,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
if (int_status & AT91_MCI_NOTBUSY) {
pr_debug("Card is ready\n");
at91_mci_update_bytes_xfered(host);
completed = 1;
}
@ -744,9 +877,21 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
if (int_status & AT91_MCI_BLKE) {
pr_debug("Block transfer has ended\n");
completed = 1;
if (host->request->data && host->request->data->blocks > 1) {
/* multi block write : complete multi write
* command and send stop */
completed = 1;
} else {
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
}
}
if (int_status & AT91_MCI_SDIOIRQA)
mmc_signal_sdio_irq(host->mmc);
if (int_status & AT91_MCI_SDIOIRQB)
mmc_signal_sdio_irq(host->mmc);
if (int_status & AT91_MCI_TXRDY)
pr_debug("Ready to transmit\n");
@ -761,10 +906,10 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
if (completed) {
pr_debug("Completed command\n");
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
at91_mci_completed_command(host);
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
at91_mci_completed_command(host, int_status);
} else
at91_mci_write(host, AT91_MCI_IDR, int_status);
at91_mci_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
return IRQ_HANDLED;
}
@ -793,25 +938,33 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
static int at91_mci_get_ro(struct mmc_host *mmc)
{
int read_only = 0;
struct at91mci_host *host = mmc_priv(mmc);
if (host->board->wp_pin) {
read_only = gpio_get_value(host->board->wp_pin);
printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc),
(read_only ? "read-only" : "read-write") );
}
else {
printk(KERN_WARNING "%s: host does not support reading read-only "
"switch. Assuming write-enable.\n", mmc_hostname(mmc));
}
return read_only;
if (host->board->wp_pin)
return !!gpio_get_value(host->board->wp_pin);
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}
static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct at91mci_host *host = mmc_priv(mmc);
pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc),
host->board->slot_b ? 'B':'A', enable ? "enable" : "disable");
at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR,
host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA);
}
static const struct mmc_host_ops at91_mci_ops = {
.request = at91_mci_request,
.set_ios = at91_mci_set_ios,
.get_ro = at91_mci_get_ro,
.enable_sdio_irq = at91_mci_enable_sdio_irq,
};
/*
@ -842,6 +995,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->f_min = 375000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_SDIO_IRQ;
mmc->max_blk_size = 4095;
mmc->max_blk_count = mmc->max_req_size;
@ -935,6 +1089,8 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc_add_host(mmc);
setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);
/*
* monitor card insertion/removal if we can
*/
@ -995,6 +1151,7 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
}
at91_mci_disable(host);
del_timer_sync(&host->timer);
mmc_remove_host(mmc);
free_irq(host->irq, host);

View File

@ -0,0 +1,91 @@
/*
* Atmel MultiMedia Card Interface driver
*
* Copyright (C) 2004-2006 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.
*/
#ifndef __DRIVERS_MMC_ATMEL_MCI_H__
#define __DRIVERS_MMC_ATMEL_MCI_H__
/* MCI Register Definitions */
#define MCI_CR 0x0000 /* Control */
# define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
# define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
# define MCI_CR_SWRST ( 1 << 7) /* Software Reset */
#define MCI_MR 0x0004 /* Mode */
# define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
# define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
# define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
#define MCI_DTOR 0x0008 /* Data Timeout */
# define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
# define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
#define MCI_SDCR 0x000c /* SD Card / SDIO */
# define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
# define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
# define MCI_SDCBUS_1BIT ( 0 << 7) /* 1-bit data bus */
# define MCI_SDCBUS_4BIT ( 1 << 7) /* 4-bit data bus */
#define MCI_ARGR 0x0010 /* Command Argument */
#define MCI_CMDR 0x0014 /* Command */
# define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
# define MCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
# define MCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
# define MCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
# define MCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
# define MCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
# define MCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
# define MCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
# define MCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
# define MCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
# define MCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
# define MCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
# define MCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
# define MCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
# define MCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
# define MCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
# define MCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
# define MCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
# define MCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
# define MCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
# define MCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
# define MCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
#define MCI_BLKR 0x0018 /* Block */
# define MCI_BCNT(x) ((x) << 0) /* Data Block Count */
# define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
#define MCI_RSPR 0x0020 /* Response 0 */
#define MCI_RSPR1 0x0024 /* Response 1 */
#define MCI_RSPR2 0x0028 /* Response 2 */
#define MCI_RSPR3 0x002c /* Response 3 */
#define MCI_RDR 0x0030 /* Receive Data */
#define MCI_TDR 0x0034 /* Transmit Data */
#define MCI_SR 0x0040 /* Status */
#define MCI_IER 0x0044 /* Interrupt Enable */
#define MCI_IDR 0x0048 /* Interrupt Disable */
#define MCI_IMR 0x004c /* Interrupt Mask */
# define MCI_CMDRDY ( 1 << 0) /* Command Ready */
# define MCI_RXRDY ( 1 << 1) /* Receiver Ready */
# define MCI_TXRDY ( 1 << 2) /* Transmitter Ready */
# define MCI_BLKE ( 1 << 3) /* Data Block Ended */
# define MCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
# define MCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
# define MCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
# define MCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
# define MCI_RINDE ( 1 << 16) /* Response Index Error */
# define MCI_RDIRE ( 1 << 17) /* Response Direction Error */
# define MCI_RCRCE ( 1 << 18) /* Response CRC Error */
# define MCI_RENDE ( 1 << 19) /* Response End Bit Error */
# define MCI_RTOE ( 1 << 20) /* Response Time-Out Error */
# define MCI_DCRCE ( 1 << 21) /* Data CRC Error */
# define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */
# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
/* Register access macros */
#define mci_readl(port,reg) \
__raw_readl((port)->regs + MCI_##reg)
#define mci_writel(port,reg,value) \
__raw_writel((value), (port)->regs + MCI_##reg)
#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */

View File

@ -0,0 +1,981 @@
/*
* Atmel MultiMedia Card Interface driver
*
* Copyright (C) 2004-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/blkdev.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
#include <asm/atmel-mci.h>
#include <asm/io.h>
#include <asm/unaligned.h>
#include <asm/arch/board.h>
#include <asm/arch/gpio.h>
#include "atmel-mci-regs.h"
#define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE)
enum {
EVENT_CMD_COMPLETE = 0,
EVENT_DATA_ERROR,
EVENT_DATA_COMPLETE,
EVENT_STOP_SENT,
EVENT_STOP_COMPLETE,
EVENT_XFER_COMPLETE,
};
struct atmel_mci {
struct mmc_host *mmc;
void __iomem *regs;
struct scatterlist *sg;
unsigned int pio_offset;
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_data *data;
u32 cmd_status;
u32 data_status;
u32 stop_status;
u32 stop_cmdr;
u32 mode_reg;
u32 sdc_reg;
struct tasklet_struct tasklet;
unsigned long pending_events;
unsigned long completed_events;
int present;
int detect_pin;
int wp_pin;
/* For detect pin debouncing */
struct timer_list detect_timer;
unsigned long bus_hz;
unsigned long mapbase;
struct clk *mck;
struct platform_device *pdev;
};
#define atmci_is_completed(host, event) \
test_bit(event, &host->completed_events)
#define atmci_test_and_clear_pending(host, event) \
test_and_clear_bit(event, &host->pending_events)
#define atmci_test_and_set_completed(host, event) \
test_and_set_bit(event, &host->completed_events)
#define atmci_set_completed(host, event) \
set_bit(event, &host->completed_events)
#define atmci_set_pending(host, event) \
set_bit(event, &host->pending_events)
#define atmci_clear_pending(host, event) \
clear_bit(event, &host->pending_events)
static void atmci_enable(struct atmel_mci *host)
{
clk_enable(host->mck);
mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg);
mci_writel(host, SDCR, host->sdc_reg);
}
static void atmci_disable(struct atmel_mci *host)
{
mci_writel(host, CR, MCI_CR_SWRST);
/* Stall until write is complete, then disable the bus clock */
mci_readl(host, SR);
clk_disable(host->mck);
}
static inline unsigned int ns_to_clocks(struct atmel_mci *host,
unsigned int ns)
{
return (ns * (host->bus_hz / 1000000) + 999) / 1000;
}
static void atmci_set_timeout(struct atmel_mci *host,
struct mmc_data *data)
{
static unsigned dtomul_to_shift[] = {
0, 4, 7, 8, 10, 12, 16, 20
};
unsigned timeout;
unsigned dtocyc;
unsigned dtomul;
timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks;
for (dtomul = 0; dtomul < 8; dtomul++) {
unsigned shift = dtomul_to_shift[dtomul];
dtocyc = (timeout + (1 << shift) - 1) >> shift;
if (dtocyc < 15)
break;
}
if (dtomul >= 8) {
dtomul = 7;
dtocyc = 15;
}
dev_vdbg(&host->mmc->class_dev, "setting timeout to %u cycles\n",
dtocyc << dtomul_to_shift[dtomul]);
mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc)));
}
/*
* Return mask with command flags to be enabled for this command.
*/
static u32 atmci_prepare_command(struct mmc_host *mmc,
struct mmc_command *cmd)
{
struct mmc_data *data;
u32 cmdr;
cmd->error = -EINPROGRESS;
cmdr = MCI_CMDR_CMDNB(cmd->opcode);
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136)
cmdr |= MCI_CMDR_RSPTYP_136BIT;
else
cmdr |= MCI_CMDR_RSPTYP_48BIT;
}
/*
* This should really be MAXLAT_5 for CMD2 and ACMD41, but
* it's too difficult to determine whether this is an ACMD or
* not. Better make it 64.
*/
cmdr |= MCI_CMDR_MAXLAT_64CYC;
if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
cmdr |= MCI_CMDR_OPDCMD;
data = cmd->data;
if (data) {
cmdr |= MCI_CMDR_START_XFER;
if (data->flags & MMC_DATA_STREAM)
cmdr |= MCI_CMDR_STREAM;
else if (data->blocks > 1)
cmdr |= MCI_CMDR_MULTI_BLOCK;
else
cmdr |= MCI_CMDR_BLOCK;
if (data->flags & MMC_DATA_READ)
cmdr |= MCI_CMDR_TRDIR_READ;
}
return cmdr;
}
static void atmci_start_command(struct atmel_mci *host,
struct mmc_command *cmd,
u32 cmd_flags)
{
/* Must read host->cmd after testing event flags */
smp_rmb();
WARN_ON(host->cmd);
host->cmd = cmd;
dev_vdbg(&host->mmc->class_dev,
"start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags);
mci_writel(host, ARGR, cmd->arg);
mci_writel(host, CMDR, cmd_flags);
}
static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data)
{
struct atmel_mci *host = mmc_priv(mmc);
atmci_start_command(host, data->stop, host->stop_cmdr);
mci_writel(host, IER, MCI_CMDRDY);
}
static void atmci_request_end(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct atmel_mci *host = mmc_priv(mmc);
WARN_ON(host->cmd || host->data);
host->mrq = NULL;
atmci_disable(host);
mmc_request_done(mmc, mrq);
}
/*
* Returns a mask of interrupt flags to be enabled after the whole
* request has been prepared.
*/
static u32 atmci_submit_data(struct mmc_host *mmc, struct mmc_data *data)
{
struct atmel_mci *host = mmc_priv(mmc);
u32 iflags;
data->error = -EINPROGRESS;
WARN_ON(host->data);
host->sg = NULL;
host->data = data;
mci_writel(host, BLKR, MCI_BCNT(data->blocks)
| MCI_BLKLEN(data->blksz));
dev_vdbg(&mmc->class_dev, "BLKR=0x%08x\n",
MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
iflags = ATMCI_DATA_ERROR_FLAGS;
host->sg = data->sg;
host->pio_offset = 0;
if (data->flags & MMC_DATA_READ)
iflags |= MCI_RXRDY;
else
iflags |= MCI_TXRDY;
return iflags;
}
static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct atmel_mci *host = mmc_priv(mmc);
struct mmc_data *data;
struct mmc_command *cmd;
u32 iflags;
u32 cmdflags = 0;
iflags = mci_readl(host, IMR);
if (iflags)
dev_warn(&mmc->class_dev, "WARNING: IMR=0x%08x\n",
mci_readl(host, IMR));
WARN_ON(host->mrq != NULL);
/*
* We may "know" the card is gone even though there's still an
* electrical connection. If so, we really need to communicate
* this to the MMC core since there won't be any more
* interrupts as the card is completely removed. Otherwise,
* the MMC core might believe the card is still there even
* though the card was just removed very slowly.
*/
if (!host->present) {
mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
return;
}
host->mrq = mrq;
host->pending_events = 0;
host->completed_events = 0;
atmci_enable(host);
/* We don't support multiple blocks of weird lengths. */
data = mrq->data;
if (data) {
if (data->blocks > 1 && data->blksz & 3)
goto fail;
atmci_set_timeout(host, data);
}
iflags = MCI_CMDRDY;
cmd = mrq->cmd;
cmdflags = atmci_prepare_command(mmc, cmd);
atmci_start_command(host, cmd, cmdflags);
if (data)
iflags |= atmci_submit_data(mmc, data);
if (mrq->stop) {
host->stop_cmdr = atmci_prepare_command(mmc, mrq->stop);
host->stop_cmdr |= MCI_CMDR_STOP_XFER;
if (!(data->flags & MMC_DATA_WRITE))
host->stop_cmdr |= MCI_CMDR_TRDIR_READ;
if (data->flags & MMC_DATA_STREAM)
host->stop_cmdr |= MCI_CMDR_STREAM;
else
host->stop_cmdr |= MCI_CMDR_MULTI_BLOCK;
}
/*
* We could have enabled interrupts earlier, but I suspect
* that would open up a nice can of interesting race
* conditions (e.g. command and data complete, but stop not
* prepared yet.)
*/
mci_writel(host, IER, iflags);
return;
fail:
atmci_disable(host);
host->mrq = NULL;
mrq->cmd->error = -EINVAL;
mmc_request_done(mmc, mrq);
}
static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct atmel_mci *host = mmc_priv(mmc);
if (ios->clock) {
u32 clkdiv;
/* Set clock rate */
clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * ios->clock) - 1;
if (clkdiv > 255) {
dev_warn(&mmc->class_dev,
"clock %u too slow; using %lu\n",
ios->clock, host->bus_hz / (2 * 256));
clkdiv = 255;
}
host->mode_reg = MCI_MR_CLKDIV(clkdiv) | MCI_MR_WRPROOF
| MCI_MR_RDPROOF;
}
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
host->sdc_reg = 0;
break;
case MMC_BUS_WIDTH_4:
host->sdc_reg = MCI_SDCBUS_4BIT;
break;
}
switch (ios->power_mode) {
case MMC_POWER_ON:
/* Send init sequence (74 clock cycles) */
atmci_enable(host);
mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT);
while (!(mci_readl(host, SR) & MCI_CMDRDY))
cpu_relax();
atmci_disable(host);
break;
default:
/*
* TODO: None of the currently available AVR32-based
* boards allow MMC power to be turned off. Implement
* power control when this can be tested properly.
*/
break;
}
}
static int atmci_get_ro(struct mmc_host *mmc)
{
int read_only = 0;
struct atmel_mci *host = mmc_priv(mmc);
if (host->wp_pin >= 0) {
read_only = gpio_get_value(host->wp_pin);
dev_dbg(&mmc->class_dev, "card is %s\n",
read_only ? "read-only" : "read-write");
} else {
dev_dbg(&mmc->class_dev,
"no pin for checking read-only switch."
" Assuming write-enable.\n");
}
return read_only;
}
static struct mmc_host_ops atmci_ops = {
.request = atmci_request,
.set_ios = atmci_set_ios,
.get_ro = atmci_get_ro,
};
static void atmci_command_complete(struct atmel_mci *host,
struct mmc_command *cmd, u32 status)
{
/* Read the response from the card (up to 16 bytes) */
cmd->resp[0] = mci_readl(host, RSPR);
cmd->resp[1] = mci_readl(host, RSPR);
cmd->resp[2] = mci_readl(host, RSPR);
cmd->resp[3] = mci_readl(host, RSPR);
if (status & MCI_RTOE)
cmd->error = -ETIMEDOUT;
else if ((cmd->flags & MMC_RSP_CRC) && (status & MCI_RCRCE))
cmd->error = -EILSEQ;
else if (status & (MCI_RINDE | MCI_RDIRE | MCI_RENDE))
cmd->error = -EIO;
else
cmd->error = 0;
if (cmd->error) {
dev_dbg(&host->mmc->class_dev,
"command error: status=0x%08x\n", status);
if (cmd->data) {
host->data = NULL;
mci_writel(host, IDR, MCI_NOTBUSY
| MCI_TXRDY | MCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS);
}
}
}
static void atmci_detect_change(unsigned long data)
{
struct atmel_mci *host = (struct atmel_mci *)data;
struct mmc_request *mrq = host->mrq;
int present;
/*
* atmci_remove() sets detect_pin to -1 before freeing the
* interrupt. We must not re-enable the interrupt if it has
* been freed.
*/
smp_rmb();
if (host->detect_pin < 0)
return;
enable_irq(gpio_to_irq(host->detect_pin));
present = !gpio_get_value(host->detect_pin);
dev_vdbg(&host->pdev->dev, "detect change: %d (was %d)\n",
present, host->present);
if (present != host->present) {
dev_dbg(&host->mmc->class_dev, "card %s\n",
present ? "inserted" : "removed");
host->present = present;
/* Reset controller if card is gone */
if (!present) {
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, IDR, ~0UL);
mci_writel(host, CR, MCI_CR_MCIEN);
}
/* Clean up queue if present */
if (mrq) {
/*
* Reset controller to terminate any ongoing
* commands or data transfers.
*/
mci_writel(host, CR, MCI_CR_SWRST);
if (!atmci_is_completed(host, EVENT_CMD_COMPLETE))
mrq->cmd->error = -ENOMEDIUM;
if (mrq->data && !atmci_is_completed(host,
EVENT_DATA_COMPLETE)) {
host->data = NULL;
mrq->data->error = -ENOMEDIUM;
}
if (mrq->stop && !atmci_is_completed(host,
EVENT_STOP_COMPLETE))
mrq->stop->error = -ENOMEDIUM;
host->cmd = NULL;
atmci_request_end(host->mmc, mrq);
}
mmc_detect_change(host->mmc, 0);
}
}
static void atmci_tasklet_func(unsigned long priv)
{
struct mmc_host *mmc = (struct mmc_host *)priv;
struct atmel_mci *host = mmc_priv(mmc);
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
dev_vdbg(&mmc->class_dev,
"tasklet: pending/completed/mask %lx/%lx/%x\n",
host->pending_events, host->completed_events,
mci_readl(host, IMR));
if (atmci_test_and_clear_pending(host, EVENT_CMD_COMPLETE)) {
/*
* host->cmd must be set to NULL before the interrupt
* handler sees EVENT_CMD_COMPLETE
*/
host->cmd = NULL;
smp_wmb();
atmci_set_completed(host, EVENT_CMD_COMPLETE);
atmci_command_complete(host, mrq->cmd, host->cmd_status);
if (!mrq->cmd->error && mrq->stop
&& atmci_is_completed(host, EVENT_XFER_COMPLETE)
&& !atmci_test_and_set_completed(host,
EVENT_STOP_SENT))
send_stop_cmd(host->mmc, mrq->data);
}
if (atmci_test_and_clear_pending(host, EVENT_STOP_COMPLETE)) {
/*
* host->cmd must be set to NULL before the interrupt
* handler sees EVENT_STOP_COMPLETE
*/
host->cmd = NULL;
smp_wmb();
atmci_set_completed(host, EVENT_STOP_COMPLETE);
atmci_command_complete(host, mrq->stop, host->stop_status);
}
if (atmci_test_and_clear_pending(host, EVENT_DATA_ERROR)) {
u32 status = host->data_status;
dev_vdbg(&mmc->class_dev, "data error: status=%08x\n", status);
atmci_set_completed(host, EVENT_DATA_ERROR);
atmci_set_completed(host, EVENT_DATA_COMPLETE);
if (status & MCI_DTOE) {
dev_dbg(&mmc->class_dev,
"data timeout error\n");
data->error = -ETIMEDOUT;
} else if (status & MCI_DCRCE) {
dev_dbg(&mmc->class_dev, "data CRC error\n");
data->error = -EILSEQ;
} else {
dev_dbg(&mmc->class_dev,
"data FIFO error (status=%08x)\n",
status);
data->error = -EIO;
}
if (host->present && data->stop
&& atmci_is_completed(host, EVENT_CMD_COMPLETE)
&& !atmci_test_and_set_completed(
host, EVENT_STOP_SENT))
send_stop_cmd(host->mmc, data);
host->data = NULL;
}
if (atmci_test_and_clear_pending(host, EVENT_DATA_COMPLETE)) {
atmci_set_completed(host, EVENT_DATA_COMPLETE);
if (!atmci_is_completed(host, EVENT_DATA_ERROR)) {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
}
host->data = NULL;
}
if (host->mrq && !host->cmd && !host->data)
atmci_request_end(mmc, host->mrq);
}
static void atmci_read_data_pio(struct atmel_mci *host)
{
struct scatterlist *sg = host->sg;
void *buf = sg_virt(sg);
unsigned int offset = host->pio_offset;
struct mmc_data *data = host->data;
u32 value;
u32 status;
unsigned int nbytes = 0;
do {
value = mci_readl(host, RDR);
if (likely(offset + 4 <= sg->length)) {
put_unaligned(value, (u32 *)(buf + offset));
offset += 4;
nbytes += 4;
if (offset == sg->length) {
host->sg = sg = sg_next(sg);
if (!sg)
goto done;
offset = 0;
buf = sg_virt(sg);
}
} else {
unsigned int remaining = sg->length - offset;
memcpy(buf + offset, &value, remaining);
nbytes += remaining;
flush_dcache_page(sg_page(sg));
host->sg = sg = sg_next(sg);
if (!sg)
goto done;
offset = 4 - remaining;
buf = sg_virt(sg);
memcpy(buf, (u8 *)&value + remaining, offset);
nbytes += offset;
}
status = mci_readl(host, SR);
if (status & ATMCI_DATA_ERROR_FLAGS) {
mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
break;
}
} while (status & MCI_RXRDY);
host->pio_offset = offset;
data->bytes_xfered += nbytes;
return;
done:
mci_writel(host, IDR, MCI_RXRDY);
mci_writel(host, IER, MCI_NOTBUSY);
data->bytes_xfered += nbytes;
atmci_set_completed(host, EVENT_XFER_COMPLETE);
if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE)
&& !atmci_test_and_set_completed(host, EVENT_STOP_SENT))
send_stop_cmd(host->mmc, data);
}
static void atmci_write_data_pio(struct atmel_mci *host)
{
struct scatterlist *sg = host->sg;
void *buf = sg_virt(sg);
unsigned int offset = host->pio_offset;
struct mmc_data *data = host->data;
u32 value;
u32 status;
unsigned int nbytes = 0;
do {
if (likely(offset + 4 <= sg->length)) {
value = get_unaligned((u32 *)(buf + offset));
mci_writel(host, TDR, value);
offset += 4;
nbytes += 4;
if (offset == sg->length) {
host->sg = sg = sg_next(sg);
if (!sg)
goto done;
offset = 0;
buf = sg_virt(sg);
}
} else {
unsigned int remaining = sg->length - offset;
value = 0;
memcpy(&value, buf + offset, remaining);
nbytes += remaining;
host->sg = sg = sg_next(sg);
if (!sg) {
mci_writel(host, TDR, value);
goto done;
}
offset = 4 - remaining;
buf = sg_virt(sg);
memcpy((u8 *)&value + remaining, buf, offset);
mci_writel(host, TDR, value);
nbytes += offset;
}
status = mci_readl(host, SR);
if (status & ATMCI_DATA_ERROR_FLAGS) {
mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
break;
}
} while (status & MCI_TXRDY);
host->pio_offset = offset;
data->bytes_xfered += nbytes;
return;
done:
mci_writel(host, IDR, MCI_TXRDY);
mci_writel(host, IER, MCI_NOTBUSY);
data->bytes_xfered += nbytes;
atmci_set_completed(host, EVENT_XFER_COMPLETE);
if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE)
&& !atmci_test_and_set_completed(host, EVENT_STOP_SENT))
send_stop_cmd(host->mmc, data);
}
static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status)
{
struct atmel_mci *host = mmc_priv(mmc);
mci_writel(host, IDR, MCI_CMDRDY);
if (atmci_is_completed(host, EVENT_STOP_SENT)) {
host->stop_status = status;
atmci_set_pending(host, EVENT_STOP_COMPLETE);
} else {
host->cmd_status = status;
atmci_set_pending(host, EVENT_CMD_COMPLETE);
}
tasklet_schedule(&host->tasklet);
}
static irqreturn_t atmci_interrupt(int irq, void *dev_id)
{
struct mmc_host *mmc = dev_id;
struct atmel_mci *host = mmc_priv(mmc);
u32 status, mask, pending;
unsigned int pass_count = 0;
spin_lock(&mmc->lock);
do {
status = mci_readl(host, SR);
mask = mci_readl(host, IMR);
pending = status & mask;
if (!pending)
break;
if (pending & ATMCI_DATA_ERROR_FLAGS) {
mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS
| MCI_RXRDY | MCI_TXRDY);
pending &= mci_readl(host, IMR);
host->data_status = status;
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
}
if (pending & MCI_NOTBUSY) {
mci_writel(host, IDR, (MCI_NOTBUSY
| ATMCI_DATA_ERROR_FLAGS));
atmci_set_pending(host, EVENT_DATA_COMPLETE);
tasklet_schedule(&host->tasklet);
}
if (pending & MCI_RXRDY)
atmci_read_data_pio(host);
if (pending & MCI_TXRDY)
atmci_write_data_pio(host);
if (pending & MCI_CMDRDY)
atmci_cmd_interrupt(mmc, status);
} while (pass_count++ < 5);
spin_unlock(&mmc->lock);
return pass_count ? IRQ_HANDLED : IRQ_NONE;
}
static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id)
{
struct mmc_host *mmc = dev_id;
struct atmel_mci *host = mmc_priv(mmc);
/*
* Disable interrupts until the pin has stabilized and check
* the state then. Use mod_timer() since we may be in the
* middle of the timer routine when this interrupt triggers.
*/
disable_irq_nosync(irq);
mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(20));
return IRQ_HANDLED;
}
static int __init atmci_probe(struct platform_device *pdev)
{
struct mci_platform_data *pdata;
struct atmel_mci *host;
struct mmc_host *mmc;
struct resource *regs;
int irq;
int ret;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
return -ENXIO;
pdata = pdev->dev.platform_data;
if (!pdata)
return -ENXIO;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
mmc = mmc_alloc_host(sizeof(struct atmel_mci), &pdev->dev);
if (!mmc)
return -ENOMEM;
host = mmc_priv(mmc);
host->pdev = pdev;
host->mmc = mmc;
host->detect_pin = pdata->detect_pin;
host->wp_pin = pdata->wp_pin;
host->mck = clk_get(&pdev->dev, "mci_clk");
if (IS_ERR(host->mck)) {
ret = PTR_ERR(host->mck);
goto err_clk_get;
}
ret = -ENOMEM;
host->regs = ioremap(regs->start, regs->end - regs->start + 1);
if (!host->regs)
goto err_ioremap;
clk_enable(host->mck);
mci_writel(host, CR, MCI_CR_SWRST);
host->bus_hz = clk_get_rate(host->mck);
clk_disable(host->mck);
host->mapbase = regs->start;
mmc->ops = &atmci_ops;
mmc->f_min = (host->bus_hz + 511) / 512;
mmc->f_max = host->bus_hz / 2;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->max_hw_segs = 64;
mmc->max_phys_segs = 64;
mmc->max_req_size = 32768 * 512;
mmc->max_blk_size = 32768;
mmc->max_blk_count = 512;
tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)mmc);
ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, mmc);
if (ret)
goto err_request_irq;
/* Assume card is present if we don't have a detect pin */
host->present = 1;
if (host->detect_pin >= 0) {
if (gpio_request(host->detect_pin, "mmc_detect")) {
dev_dbg(&mmc->class_dev, "no detect pin available\n");
host->detect_pin = -1;
} else {
host->present = !gpio_get_value(host->detect_pin);
}
}
if (host->wp_pin >= 0) {
if (gpio_request(host->wp_pin, "mmc_wp")) {
dev_dbg(&mmc->class_dev, "no WP pin available\n");
host->wp_pin = -1;
}
}
platform_set_drvdata(pdev, host);
mmc_add_host(mmc);
if (host->detect_pin >= 0) {
setup_timer(&host->detect_timer, atmci_detect_change,
(unsigned long)host);
ret = request_irq(gpio_to_irq(host->detect_pin),
atmci_detect_interrupt,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"mmc-detect", mmc);
if (ret) {
dev_dbg(&mmc->class_dev,
"could not request IRQ %d for detect pin\n",
gpio_to_irq(host->detect_pin));
gpio_free(host->detect_pin);
host->detect_pin = -1;
}
}
dev_info(&mmc->class_dev,
"Atmel MCI controller at 0x%08lx irq %d\n",
host->mapbase, irq);
return 0;
err_request_irq:
iounmap(host->regs);
err_ioremap:
clk_put(host->mck);
err_clk_get:
mmc_free_host(mmc);
return ret;
}
static int __exit atmci_remove(struct platform_device *pdev)
{
struct atmel_mci *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (host) {
if (host->detect_pin >= 0) {
int pin = host->detect_pin;
/* Make sure the timer doesn't enable the interrupt */
host->detect_pin = -1;
smp_wmb();
free_irq(gpio_to_irq(pin), host->mmc);
del_timer_sync(&host->detect_timer);
gpio_free(pin);
}
mmc_remove_host(host->mmc);
clk_enable(host->mck);
mci_writel(host, IDR, ~0UL);
mci_writel(host, CR, MCI_CR_MCIDIS);
mci_readl(host, SR);
clk_disable(host->mck);
if (host->wp_pin >= 0)
gpio_free(host->wp_pin);
free_irq(platform_get_irq(pdev, 0), host->mmc);
iounmap(host->regs);
clk_put(host->mck);
mmc_free_host(host->mmc);
}
return 0;
}
static struct platform_driver atmci_driver = {
.remove = __exit_p(atmci_remove),
.driver = {
.name = "atmel_mci",
},
};
static int __init atmci_init(void)
{
return platform_driver_probe(&atmci_driver, atmci_probe);
}
static void __exit atmci_exit(void)
{
platform_driver_unregister(&atmci_driver);
}
module_init(atmci_init);
module_exit(atmci_exit);
MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver");
MODULE_AUTHOR("Haavard Skinnemoen <haavard.skinnemoen@atmel.com>");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +0,0 @@
#ifndef _AU1XMMC_H_
#define _AU1XMMC_H_
/* Hardware definitions */
#define AU1XMMC_DESCRIPTOR_COUNT 1
#define AU1XMMC_DESCRIPTOR_SIZE 2048
#define AU1XMMC_OCR ( MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)
/* Easy access macros */
#define HOST_STATUS(h) ((h)->iobase + SD_STATUS)
#define HOST_CONFIG(h) ((h)->iobase + SD_CONFIG)
#define HOST_ENABLE(h) ((h)->iobase + SD_ENABLE)
#define HOST_TXPORT(h) ((h)->iobase + SD_TXPORT)
#define HOST_RXPORT(h) ((h)->iobase + SD_RXPORT)
#define HOST_CMDARG(h) ((h)->iobase + SD_CMDARG)
#define HOST_BLKSIZE(h) ((h)->iobase + SD_BLKSIZE)
#define HOST_CMD(h) ((h)->iobase + SD_CMD)
#define HOST_CONFIG2(h) ((h)->iobase + SD_CONFIG2)
#define HOST_TIMEOUT(h) ((h)->iobase + SD_TIMEOUT)
#define HOST_DEBUG(h) ((h)->iobase + SD_DEBUG)
#define DMA_CHANNEL(h) \
( ((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
/* This gives us a hard value for the stop command that we can write directly
* to the command register
*/
#define STOP_CMD (SD_CMD_RT_1B|SD_CMD_CT_7|(0xC << SD_CMD_CI_SHIFT)|SD_CMD_GO)
/* This is the set of interrupts that we configure by default */
#if 0
#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_DD | \
SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I)
#endif
#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | \
SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I)
/* The poll event (looking for insert/remove events runs twice a second */
#define AU1XMMC_DETECT_TIMEOUT (HZ/2)
struct au1xmmc_host {
struct mmc_host *mmc;
struct mmc_request *mrq;
u32 id;
u32 flags;
u32 iobase;
u32 clock;
u32 bus_width;
u32 power_mode;
int status;
struct {
int len;
int dir;
} dma;
struct {
int index;
int offset;
int len;
} pio;
u32 tx_chan;
u32 rx_chan;
struct timer_list timer;
struct tasklet_struct finish_task;
struct tasklet_struct data_task;
spinlock_t lock;
};
/* Status flags used by the host structure */
#define HOST_F_XMIT 0x0001
#define HOST_F_RECV 0x0002
#define HOST_F_DMA 0x0010
#define HOST_F_ACTIVE 0x0100
#define HOST_F_STOP 0x1000
#define HOST_S_IDLE 0x0001
#define HOST_S_CMD 0x0002
#define HOST_S_DATA 0x0003
#define HOST_S_STOP 0x0004
#endif

View File

@ -892,9 +892,12 @@ static int imxmci_get_ro(struct mmc_host *mmc)
struct imxmci_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_ro)
return host->pdata->get_ro(mmc_dev(mmc));
/* Host doesn't support read only detection so assume writeable */
return 0;
return !!host->pdata->get_ro(mmc_dev(mmc));
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}

View File

@ -1126,16 +1126,28 @@ static int mmc_spi_get_ro(struct mmc_host *mmc)
struct mmc_spi_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_ro)
return host->pdata->get_ro(mmc->parent);
/* board doesn't support read only detection; assume writeable */
return 0;
return !!host->pdata->get_ro(mmc->parent);
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}
static int mmc_spi_get_cd(struct mmc_host *mmc)
{
struct mmc_spi_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_cd)
return !!host->pdata->get_cd(mmc->parent);
return -ENOSYS;
}
static const struct mmc_host_ops mmc_spi_ops = {
.request = mmc_spi_request,
.set_ios = mmc_spi_set_ios,
.get_ro = mmc_spi_get_ro,
.get_cd = mmc_spi_get_cd,
};
@ -1240,10 +1252,7 @@ static int mmc_spi_probe(struct spi_device *spi)
mmc->ops = &mmc_spi_ops;
mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
/* As long as we keep track of the number of successfully
* transmitted blocks, we're good for multiwrite.
*/
mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;
mmc->caps = MMC_CAP_SPI;
/* SPI doesn't need the lowspeed device identification thing for
* MMC or SD cards, since it never comes up in open drain mode.
@ -1319,17 +1328,23 @@ static int mmc_spi_probe(struct spi_device *spi)
goto fail_glue_init;
}
/* pass platform capabilities, if any */
if (host->pdata)
mmc->caps |= host->pdata->caps;
status = mmc_add_host(mmc);
if (status != 0)
goto fail_add_host;
dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n",
dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n",
mmc->class_dev.bus_id,
host->dma_dev ? "" : ", no DMA",
(host->pdata && host->pdata->get_ro)
? "" : ", no WP",
(host->pdata && host->pdata->setpower)
? "" : ", no poweroff");
? "" : ", no poweroff",
(mmc->caps & MMC_CAP_NEEDS_POLL)
? ", cd polling" : "");
return 0;
fail_add_host:

View File

@ -535,7 +535,6 @@ static int mmci_probe(struct amba_device *dev, void *id)
mmc->f_min = (host->mclk + 511) / 512;
mmc->f_max = min(host->mclk, fmax);
mmc->ocr_avail = plat->ocr_mask;
mmc->caps = MMC_CAP_MULTIWRITE;
/*
* We can do SGIO

View File

@ -1317,7 +1317,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
host->slots[id] = slot;
mmc->caps = MMC_CAP_MULTIWRITE;
mmc->caps = 0;
if (host->pdata->conf.wire4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

View File

@ -374,9 +374,12 @@ static int pxamci_get_ro(struct mmc_host *mmc)
struct pxamci_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_ro)
return host->pdata->get_ro(mmc_dev(mmc));
/* Host doesn't support read only detection so assume writeable */
return 0;
return !!host->pdata->get_ro(mmc_dev(mmc));
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}
static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

1446
drivers/mmc/host/s3cmci.c Normal file

File diff suppressed because it is too large Load Diff

70
drivers/mmc/host/s3cmci.h Normal file
View File

@ -0,0 +1,70 @@
/*
* linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
*
* Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* FIXME: DMA Resource management ?! */
#define S3CMCI_DMA 0
enum s3cmci_waitfor {
COMPLETION_NONE,
COMPLETION_FINALIZE,
COMPLETION_CMDSENT,
COMPLETION_RSPFIN,
COMPLETION_XFERFINISH,
COMPLETION_XFERFINISH_RSPFIN,
};
struct s3cmci_host {
struct platform_device *pdev;
struct s3c24xx_mci_pdata *pdata;
struct mmc_host *mmc;
struct resource *mem;
struct clk *clk;
void __iomem *base;
int irq;
int irq_cd;
int dma;
unsigned long clk_rate;
unsigned long clk_div;
unsigned long real_rate;
u8 prescaler;
int is2440;
unsigned sdiimsk;
unsigned sdidata;
int dodma;
int dmatogo;
struct mmc_request *mrq;
int cmd_is_stop;
spinlock_t complete_lock;
enum s3cmci_waitfor complete_what;
int dma_complete;
u32 pio_sgptr;
u32 pio_words;
u32 pio_count;
u32 *pio_ptr;
#define XFER_NONE 0
#define XFER_READ 1
#define XFER_WRITE 2
u32 pio_active;
int bus_width;
char dbgmsg_cmd[301];
char dbgmsg_dat[301];
char *status;
unsigned int ccnt, dcnt;
struct tasklet_struct pio_tasklet;
};

View File

@ -0,0 +1,732 @@
/* linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus 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.
*
* Thanks to the following companies for their support:
*
* - JMicron (hardware and technical support)
*/
#include <linux/delay.h>
#include <linux/highmem.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
#include "sdhci.h"
/*
* PCI registers
*/
#define PCI_SDHCI_IFPIO 0x00
#define PCI_SDHCI_IFDMA 0x01
#define PCI_SDHCI_IFVENDOR 0x02
#define PCI_SLOT_INFO 0x40 /* 8 bits */
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
#define MAX_SLOTS 8
struct sdhci_pci_chip;
struct sdhci_pci_slot;
struct sdhci_pci_fixes {
unsigned int quirks;
int (*probe)(struct sdhci_pci_chip*);
int (*probe_slot)(struct sdhci_pci_slot*);
void (*remove_slot)(struct sdhci_pci_slot*, int);
int (*suspend)(struct sdhci_pci_chip*,
pm_message_t);
int (*resume)(struct sdhci_pci_chip*);
};
struct sdhci_pci_slot {
struct sdhci_pci_chip *chip;
struct sdhci_host *host;
int pci_bar;
};
struct sdhci_pci_chip {
struct pci_dev *pdev;
unsigned int quirks;
const struct sdhci_pci_fixes *fixes;
int num_slots; /* Slots on controller */
struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
};
/*****************************************************************************\
* *
* Hardware specific quirk handling *
* *
\*****************************************************************************/
static int ricoh_probe(struct sdhci_pci_chip *chip)
{
if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
chip->quirks |= SDHCI_QUIRK_CLOCK_BEFORE_RESET;
if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)
chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET;
return 0;
}
static const struct sdhci_pci_fixes sdhci_ricoh = {
.probe = ricoh_probe,
.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR,
};
static const struct sdhci_pci_fixes sdhci_ene_712 = {
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_BROKEN_DMA,
};
static const struct sdhci_pci_fixes sdhci_ene_714 = {
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
SDHCI_QUIRK_BROKEN_DMA,
};
static const struct sdhci_pci_fixes sdhci_cafe = {
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
};
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
{
u8 scratch;
int ret;
ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch);
if (ret)
return ret;
/*
* Turn PMOS on [bit 0], set over current detection to 2.4 V
* [bit 1:2] and enable over current debouncing [bit 6].
*/
if (on)
scratch |= 0x47;
else
scratch &= ~0x47;
ret = pci_write_config_byte(chip->pdev, 0xAE, scratch);
if (ret)
return ret;
return 0;
}
static int jmicron_probe(struct sdhci_pci_chip *chip)
{
int ret;
if (chip->pdev->revision == 0) {
chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE |
SDHCI_QUIRK_32BIT_ADMA_SIZE |
SDHCI_QUIRK_RESET_AFTER_REQUEST;
}
/*
* JMicron chips can have two interfaces to the same hardware
* in order to work around limitations in Microsoft's driver.
* We need to make sure we only bind to one of them.
*
* This code assumes two things:
*
* 1. The PCI code adds subfunctions in order.
*
* 2. The MMC interface has a lower subfunction number
* than the SD interface.
*/
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) {
struct pci_dev *sd_dev;
sd_dev = NULL;
while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON,
PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) {
if ((PCI_SLOT(chip->pdev->devfn) ==
PCI_SLOT(sd_dev->devfn)) &&
(chip->pdev->bus == sd_dev->bus))
break;
}
if (sd_dev) {
pci_dev_put(sd_dev);
dev_info(&chip->pdev->dev, "Refusing to bind to "
"secondary interface.\n");
return -ENODEV;
}
}
/*
* JMicron chips need a bit of a nudge to enable the power
* output pins.
*/
ret = jmicron_pmos(chip, 1);
if (ret) {
dev_err(&chip->pdev->dev, "Failure enabling card power\n");
return ret;
}
return 0;
}
static void jmicron_enable_mmc(struct sdhci_host *host, int on)
{
u8 scratch;
scratch = readb(host->ioaddr + 0xC0);
if (on)
scratch |= 0x01;
else
scratch &= ~0x01;
writeb(scratch, host->ioaddr + 0xC0);
}
static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
{
if (slot->chip->pdev->revision == 0) {
u16 version;
version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION);
version = (version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT;
/*
* Older versions of the chip have lots of nasty glitches
* in the ADMA engine. It's best just to avoid it
* completely.
*/
if (version < 0xAC)
slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
}
/*
* The secondary interface requires a bit set to get the
* interrupts.
*/
if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
jmicron_enable_mmc(slot->host, 1);
return 0;
}
static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead)
{
if (dead)
return;
if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
jmicron_enable_mmc(slot->host, 0);
}
static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state)
{
int i;
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) {
for (i = 0;i < chip->num_slots;i++)
jmicron_enable_mmc(chip->slots[i]->host, 0);
}
return 0;
}
static int jmicron_resume(struct sdhci_pci_chip *chip)
{
int ret, i;
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) {
for (i = 0;i < chip->num_slots;i++)
jmicron_enable_mmc(chip->slots[i]->host, 1);
}
ret = jmicron_pmos(chip, 1);
if (ret) {
dev_err(&chip->pdev->dev, "Failure enabling card power\n");
return ret;
}
return 0;
}
static const struct sdhci_pci_fixes sdhci_jmicron = {
.probe = jmicron_probe,
.probe_slot = jmicron_probe_slot,
.remove_slot = jmicron_remove_slot,
.suspend = jmicron_suspend,
.resume = jmicron_resume,
};
static const struct pci_device_id pci_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C822,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_ricoh,
},
{
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB712_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_ene_712,
},
{
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB712_SD_2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_ene_712,
},
{
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB714_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_ene_714,
},
{
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB714_SD_2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_ene_714,
},
{
.vendor = PCI_VENDOR_ID_MARVELL,
.device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_cafe,
},
{
.vendor = PCI_VENDOR_ID_JMICRON,
.device = PCI_DEVICE_ID_JMICRON_JMB38X_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_jmicron,
},
{
.vendor = PCI_VENDOR_ID_JMICRON,
.device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_jmicron,
},
{ /* Generic SD host controller */
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
},
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(pci, pci_ids);
/*****************************************************************************\
* *
* SDHCI core callbacks *
* *
\*****************************************************************************/
static int sdhci_pci_enable_dma(struct sdhci_host *host)
{
struct sdhci_pci_slot *slot;
struct pci_dev *pdev;
int ret;
slot = sdhci_priv(host);
pdev = slot->chip->pdev;
if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) &&
((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
(host->flags & SDHCI_USE_DMA)) {
dev_warn(&pdev->dev, "Will use DMA mode even though HW "
"doesn't fully claim to support it.\n");
}
ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (ret)
return ret;
pci_set_master(pdev);
return 0;
}
static struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma,
};
/*****************************************************************************\
* *
* Suspend/resume *
* *
\*****************************************************************************/
#ifdef CONFIG_PM
static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
{
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
int i, ret;
chip = pci_get_drvdata(pdev);
if (!chip)
return 0;
for (i = 0;i < chip->num_slots;i++) {
slot = chip->slots[i];
if (!slot)
continue;
ret = sdhci_suspend_host(slot->host, state);
if (ret) {
for (i--;i >= 0;i--)
sdhci_resume_host(chip->slots[i]->host);
return ret;
}
}
if (chip->fixes && chip->fixes->suspend) {
ret = chip->fixes->suspend(chip, state);
if (ret) {
for (i = chip->num_slots - 1;i >= 0;i--)
sdhci_resume_host(chip->slots[i]->host);
return ret;
}
}
pci_save_state(pdev);
pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
static int sdhci_pci_resume (struct pci_dev *pdev)
{
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
int i, ret;
chip = pci_get_drvdata(pdev);
if (!chip)
return 0;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
ret = pci_enable_device(pdev);
if (ret)
return ret;
if (chip->fixes && chip->fixes->resume) {
ret = chip->fixes->resume(chip);
if (ret)
return ret;
}
for (i = 0;i < chip->num_slots;i++) {
slot = chip->slots[i];
if (!slot)
continue;
ret = sdhci_resume_host(slot->host);
if (ret)
return ret;
}
return 0;
}
#else /* CONFIG_PM */
#define sdhci_pci_suspend NULL
#define sdhci_pci_resume NULL
#endif /* CONFIG_PM */
/*****************************************************************************\
* *
* Device probing/removal *
* *
\*****************************************************************************/
static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar)
{
struct sdhci_pci_slot *slot;
struct sdhci_host *host;
resource_size_t addr;
int ret;
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
return ERR_PTR(-ENODEV);
}
if (pci_resource_len(pdev, bar) != 0x100) {
dev_err(&pdev->dev, "Invalid iomem size. You may "
"experience problems.\n");
}
if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n");
return ERR_PTR(-ENODEV);
}
if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
dev_err(&pdev->dev, "Unknown interface. Aborting.\n");
return ERR_PTR(-ENODEV);
}
host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
if (IS_ERR(host)) {
ret = PTR_ERR(host);
goto unmap;
}
slot = sdhci_priv(host);
slot->chip = chip;
slot->host = host;
slot->pci_bar = bar;
host->hw_name = "PCI";
host->ops = &sdhci_pci_ops;
host->quirks = chip->quirks;
host->irq = pdev->irq;
ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
if (ret) {
dev_err(&pdev->dev, "cannot request region\n");
return ERR_PTR(ret);
}
addr = pci_resource_start(pdev, bar);
host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar));
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
goto release;
}
if (chip->fixes && chip->fixes->probe_slot) {
ret = chip->fixes->probe_slot(slot);
if (ret)
goto unmap;
}
ret = sdhci_add_host(host);
if (ret)
goto remove;
return slot;
remove:
if (chip->fixes && chip->fixes->remove_slot)
chip->fixes->remove_slot(slot, 0);
unmap:
iounmap(host->ioaddr);
release:
pci_release_region(pdev, bar);
sdhci_free_host(host);
return ERR_PTR(ret);
}
static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
{
int dead;
u32 scratch;
dead = 0;
scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1)
dead = 1;
sdhci_remove_host(slot->host, dead);
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
slot->chip->fixes->remove_slot(slot, dead);
pci_release_region(slot->chip->pdev, slot->pci_bar);
sdhci_free_host(slot->host);
}
static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
u8 slots, rev, first_bar;
int ret, i;
BUG_ON(pdev == NULL);
BUG_ON(ent == NULL);
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n",
(int)pdev->vendor, (int)pdev->device, (int)rev);
ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
if (ret)
return ret;
slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
if (slots == 0)
return -ENODEV;
BUG_ON(slots > MAX_SLOTS);
ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
if (ret)
return ret;
first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
if (first_bar > 5) {
dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n");
return -ENODEV;
}
ret = pci_enable_device(pdev);
if (ret)
return ret;
chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
if (!chip) {
ret = -ENOMEM;
goto err;
}
chip->pdev = pdev;
chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data;
if (chip->fixes)
chip->quirks = chip->fixes->quirks;
chip->num_slots = slots;
pci_set_drvdata(pdev, chip);
if (chip->fixes && chip->fixes->probe) {
ret = chip->fixes->probe(chip);
if (ret)
goto free;
}
for (i = 0;i < slots;i++) {
slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
if (IS_ERR(slot)) {
for (i--;i >= 0;i--)
sdhci_pci_remove_slot(chip->slots[i]);
ret = PTR_ERR(slot);
goto free;
}
chip->slots[i] = slot;
}
return 0;
free:
pci_set_drvdata(pdev, NULL);
kfree(chip);
err:
pci_disable_device(pdev);
return ret;
}
static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
{
int i;
struct sdhci_pci_chip *chip;
chip = pci_get_drvdata(pdev);
if (chip) {
for (i = 0;i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]);
pci_set_drvdata(pdev, NULL);
kfree(chip);
}
pci_disable_device(pdev);
}
static struct pci_driver sdhci_driver = {
.name = "sdhci-pci",
.id_table = pci_ids,
.probe = sdhci_pci_probe,
.remove = __devexit_p(sdhci_pci_remove),
.suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume,
};
/*****************************************************************************\
* *
* Driver init/exit *
* *
\*****************************************************************************/
static int __init sdhci_drv_init(void)
{
return pci_register_driver(&sdhci_driver);
}
static void __exit sdhci_drv_exit(void)
{
pci_unregister_driver(&sdhci_driver);
}
module_init(sdhci_drv_init);
module_exit(sdhci_drv_exit);
MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -9,18 +9,6 @@
* your option) any later version.
*/
/*
* PCI registers
*/
#define PCI_SDHCI_IFPIO 0x00
#define PCI_SDHCI_IFDMA 0x01
#define PCI_SDHCI_IFVENDOR 0x02
#define PCI_SLOT_INFO 0x40 /* 8 bits */
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
/*
* Controller registers
*/
@ -72,6 +60,11 @@
#define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02
#define SDHCI_CTRL_HISPD 0x04
#define SDHCI_CTRL_DMA_MASK 0x18
#define SDHCI_CTRL_SDMA 0x00
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
#define SDHCI_POWER_CONTROL 0x29
#define SDHCI_POWER_ON 0x01
@ -117,6 +110,7 @@
#define SDHCI_INT_DATA_END_BIT 0x00400000
#define SDHCI_INT_BUS_POWER 0x00800000
#define SDHCI_INT_ACMD12ERR 0x01000000
#define SDHCI_INT_ADMA_ERROR 0x02000000
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
@ -140,11 +134,14 @@
#define SDHCI_CLOCK_BASE_SHIFT 8
#define SDHCI_MAX_BLOCK_MASK 0x00030000
#define SDHCI_MAX_BLOCK_SHIFT 16
#define SDHCI_CAN_DO_ADMA2 0x00080000
#define SDHCI_CAN_DO_ADMA1 0x00100000
#define SDHCI_CAN_DO_HISPD 0x00200000
#define SDHCI_CAN_DO_DMA 0x00400000
#define SDHCI_CAN_VDD_330 0x01000000
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
/* 44-47 reserved for more caps */
@ -152,7 +149,16 @@
/* 4C-4F reserved for more max current */
/* 50-FB reserved */
#define SDHCI_SET_ACMD12_ERROR 0x50
#define SDHCI_SET_INT_ERROR 0x52
#define SDHCI_ADMA_ERROR 0x54
/* 55-57 reserved */
#define SDHCI_ADMA_ADDRESS 0x58
/* 60-FB reserved */
#define SDHCI_SLOT_INT_STATUS 0xFC
@ -161,11 +167,50 @@
#define SDHCI_VENDOR_VER_SHIFT 8
#define SDHCI_SPEC_VER_MASK 0x00FF
#define SDHCI_SPEC_VER_SHIFT 0
#define SDHCI_SPEC_100 0
#define SDHCI_SPEC_200 1
struct sdhci_chip;
struct sdhci_ops;
struct sdhci_host {
struct sdhci_chip *chip;
/* 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)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
const struct sdhci_ops *ops; /* Low level hw interface */
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
#ifdef CONFIG_LEDS_CLASS
@ -176,7 +221,11 @@ struct sdhci_host {
int flags; /* Host attributes */
#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */
#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */
#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) */
@ -194,22 +243,41 @@ struct sdhci_host {
int offset; /* Offset into current sg */
int remain; /* Bytes left in current */
int irq; /* Device IRQ */
int bar; /* PCI BAR index */
unsigned long addr; /* Bus address */
void __iomem * ioaddr; /* Mapped address */
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 long private[0] ____cacheline_aligned;
};
struct sdhci_chip {
struct pci_dev *pdev;
unsigned long quirks;
int num_slots; /* Slots on controller */
struct sdhci_host *hosts[0]; /* Pointers to hosts */
struct sdhci_ops {
int (*enable_dma)(struct sdhci_host *host);
};
extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size);
extern void sdhci_free_host(struct sdhci_host *host);
static inline void *sdhci_priv(struct sdhci_host *host)
{
return (void *)host->private;
}
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
#ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);
extern int sdhci_resume_host(struct sdhci_host *host);
#endif

View File

@ -0,0 +1,575 @@
/*
* sdricoh_cs.c - driver for Ricoh Secure Digital Card Readers that can be
* found on some Ricoh RL5c476 II cardbus bridge
*
* Copyright (C) 2006 - 2008 Sascha Sommer <saschasommer@freenet.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, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*
#define DEBUG
#define VERBOSE_DEBUG
*/
#include <linux/delay.h>
#include <linux/highmem.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/scatterlist.h>
#include <linux/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <linux/io.h>
#include <linux/mmc/host.h>
#define DRIVER_NAME "sdricoh_cs"
static unsigned int switchlocked;
/* i/o region */
#define SDRICOH_PCI_REGION 0
#define SDRICOH_PCI_REGION_SIZE 0x1000
/* registers */
#define R104_VERSION 0x104
#define R200_CMD 0x200
#define R204_CMD_ARG 0x204
#define R208_DATAIO 0x208
#define R20C_RESP 0x20c
#define R21C_STATUS 0x21c
#define R2E0_INIT 0x2e0
#define R2E4_STATUS_RESP 0x2e4
#define R2F0_RESET 0x2f0
#define R224_MODE 0x224
#define R226_BLOCKSIZE 0x226
#define R228_POWER 0x228
#define R230_DATA 0x230
/* flags for the R21C_STATUS register */
#define STATUS_CMD_FINISHED 0x00000001
#define STATUS_TRANSFER_FINISHED 0x00000004
#define STATUS_CARD_INSERTED 0x00000020
#define STATUS_CARD_LOCKED 0x00000080
#define STATUS_CMD_TIMEOUT 0x00400000
#define STATUS_READY_TO_READ 0x01000000
#define STATUS_READY_TO_WRITE 0x02000000
#define STATUS_BUSY 0x40000000
/* timeouts */
#define INIT_TIMEOUT 100
#define CMD_TIMEOUT 100000
#define TRANSFER_TIMEOUT 100000
#define BUSY_TIMEOUT 32767
/* list of supported pcmcia devices */
static struct pcmcia_device_id pcmcia_ids[] = {
/* vendor and device strings followed by their crc32 hashes */
PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay1Controller", 0xd9f522ed,
0xc3901202),
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, pcmcia_ids);
/* mmc privdata */
struct sdricoh_host {
struct device *dev;
struct mmc_host *mmc; /* MMC structure */
unsigned char __iomem *iobase;
struct pci_dev *pci_dev;
int app_cmd;
};
/***************** register i/o helper functions *****************************/
static inline unsigned int sdricoh_readl(struct sdricoh_host *host,
unsigned int reg)
{
unsigned int value = readl(host->iobase + reg);
dev_vdbg(host->dev, "rl %x 0x%x\n", reg, value);
return value;
}
static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg,
unsigned int value)
{
writel(value, host->iobase + reg);
dev_vdbg(host->dev, "wl %x 0x%x\n", reg, value);
}
static inline unsigned int sdricoh_readw(struct sdricoh_host *host,
unsigned int reg)
{
unsigned int value = readw(host->iobase + reg);
dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
return value;
}
static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg,
unsigned short value)
{
writew(value, host->iobase + reg);
dev_vdbg(host->dev, "ww %x 0x%x\n", reg, value);
}
static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
unsigned int reg)
{
unsigned int value = readb(host->iobase + reg);
dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
return value;
}
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
unsigned int timeout){
unsigned int loop;
unsigned int status = 0;
struct device *dev = host->dev;
for (loop = 0; loop < timeout; loop++) {
status = sdricoh_readl(host, R21C_STATUS);
sdricoh_writel(host, R2E4_STATUS_RESP, status);
if (status & wanted)
break;
}
if (loop == timeout) {
dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
return -ETIMEDOUT;
}
/* do not do this check in the loop as some commands fail otherwise */
if (status & 0x7F0000) {
dev_err(dev, "waiting for status bit %x failed\n", wanted);
return -EINVAL;
}
return 0;
}
static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode,
unsigned int arg)
{
unsigned int status;
int result = 0;
unsigned int loop = 0;
/* reset status reg? */
sdricoh_writel(host, R21C_STATUS, 0x18);
/* fill parameters */
sdricoh_writel(host, R204_CMD_ARG, arg);
sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
/* wait for command completion */
if (opcode) {
for (loop = 0; loop < CMD_TIMEOUT; loop++) {
status = sdricoh_readl(host, R21C_STATUS);
sdricoh_writel(host, R2E4_STATUS_RESP, status);
if (status & STATUS_CMD_FINISHED)
break;
}
/* don't check for timeout in the loop it is not always
reset correctly
*/
if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT)
result = -ETIMEDOUT;
}
return result;
}
static int sdricoh_reset(struct sdricoh_host *host)
{
dev_dbg(host->dev, "reset\n");
sdricoh_writel(host, R2F0_RESET, 0x10001);
sdricoh_writel(host, R2E0_INIT, 0x10000);
if (sdricoh_readl(host, R2E0_INIT) != 0x10000)
return -EIO;
sdricoh_writel(host, R2E0_INIT, 0x10007);
sdricoh_writel(host, R224_MODE, 0x2000000);
sdricoh_writel(host, R228_POWER, 0xe0);
/* status register ? */
sdricoh_writel(host, R21C_STATUS, 0x18);
return 0;
}
static int sdricoh_blockio(struct sdricoh_host *host, int read,
u8 *buf, int len)
{
int size;
u32 data = 0;
/* wait until the data is available */
if (read) {
if (sdricoh_query_status(host, STATUS_READY_TO_READ,
TRANSFER_TIMEOUT))
return -ETIMEDOUT;
sdricoh_writel(host, R21C_STATUS, 0x18);
/* read data */
while (len) {
data = sdricoh_readl(host, R230_DATA);
size = min(len, 4);
len -= size;
while (size) {
*buf = data & 0xFF;
buf++;
data >>= 8;
size--;
}
}
} else {
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
TRANSFER_TIMEOUT))
return -ETIMEDOUT;
sdricoh_writel(host, R21C_STATUS, 0x18);
/* write data */
while (len) {
size = min(len, 4);
len -= size;
while (size) {
data >>= 8;
data |= (u32)*buf << 24;
buf++;
size--;
}
sdricoh_writel(host, R230_DATA, data);
}
}
if (len)
return -EIO;
return 0;
}
static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdricoh_host *host = mmc_priv(mmc);
struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = cmd->data;
struct device *dev = host->dev;
unsigned char opcode = cmd->opcode;
int i;
dev_dbg(dev, "=============================\n");
dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode);
sdricoh_writel(host, R21C_STATUS, 0x18);
/* MMC_APP_CMDs need some special handling */
if (host->app_cmd) {
opcode |= 64;
host->app_cmd = 0;
} else if (opcode == 55)
host->app_cmd = 1;
/* read/write commands seem to require this */
if (data) {
sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
sdricoh_writel(host, R208_DATAIO, 0);
}
cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg);
/* read response buffer */
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0; i < 4; i++) {
cmd->resp[i] =
sdricoh_readl(host,
R20C_RESP + (3 - i) * 4) << 8;
if (i != 3)
cmd->resp[i] |=
sdricoh_readb(host, R20C_RESP +
(3 - i) * 4 - 1);
}
} else
cmd->resp[0] = sdricoh_readl(host, R20C_RESP);
}
/* transfer data */
if (data && cmd->error == 0) {
dev_dbg(dev, "transfer: blksz %i blocks %i sg_len %i "
"sg length %i\n", data->blksz, data->blocks,
data->sg_len, data->sg->length);
/* enter data reading mode */
sdricoh_writel(host, R21C_STATUS, 0x837f031e);
for (i = 0; i < data->blocks; i++) {
size_t len = data->blksz;
u8 *buf;
struct page *page;
int result;
page = sg_page(data->sg);
buf = kmap(page) + data->sg->offset + (len * i);
result =
sdricoh_blockio(host,
data->flags & MMC_DATA_READ, buf, len);
kunmap(page);
flush_dcache_page(page);
if (result) {
dev_err(dev, "sdricoh_request: cmd %i "
"block transfer failed\n", cmd->opcode);
cmd->error = result;
break;
} else
data->bytes_xfered += len;
}
sdricoh_writel(host, R208_DATAIO, 1);
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
TRANSFER_TIMEOUT)) {
dev_err(dev, "sdricoh_request: transfer end error\n");
cmd->error = -EINVAL;
}
}
/* FIXME check busy flag */
mmc_request_done(mmc, mrq);
dev_dbg(dev, "=============================\n");
}
static void sdricoh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdricoh_host *host = mmc_priv(mmc);
dev_dbg(host->dev, "set_ios\n");
if (ios->power_mode == MMC_POWER_ON) {
sdricoh_writel(host, R228_POWER, 0xc0e0);
if (ios->bus_width == MMC_BUS_WIDTH_4) {
sdricoh_writel(host, R224_MODE, 0x2000300);
sdricoh_writel(host, R228_POWER, 0x40e0);
} else {
sdricoh_writel(host, R224_MODE, 0x2000340);
}
} else if (ios->power_mode == MMC_POWER_UP) {
sdricoh_writel(host, R224_MODE, 0x2000320);
sdricoh_writel(host, R228_POWER, 0xe0);
}
}
static int sdricoh_get_ro(struct mmc_host *mmc)
{
struct sdricoh_host *host = mmc_priv(mmc);
unsigned int status;
status = sdricoh_readl(host, R21C_STATUS);
sdricoh_writel(host, R2E4_STATUS_RESP, status);
/* some notebooks seem to have the locked flag switched */
if (switchlocked)
return !(status & STATUS_CARD_LOCKED);
return (status & STATUS_CARD_LOCKED);
}
static struct mmc_host_ops sdricoh_ops = {
.request = sdricoh_request,
.set_ios = sdricoh_set_ios,
.get_ro = sdricoh_get_ro,
};
/* initialize the control and register it to the mmc framework */
static int sdricoh_init_mmc(struct pci_dev *pci_dev,
struct pcmcia_device *pcmcia_dev)
{
int result = 0;
void __iomem *iobase = NULL;
struct mmc_host *mmc = NULL;
struct sdricoh_host *host = NULL;
struct device *dev = &pcmcia_dev->dev;
/* map iomem */
if (pci_resource_len(pci_dev, SDRICOH_PCI_REGION) !=
SDRICOH_PCI_REGION_SIZE) {
dev_dbg(dev, "unexpected pci resource len\n");
return -ENODEV;
}
iobase =
pci_iomap(pci_dev, SDRICOH_PCI_REGION, SDRICOH_PCI_REGION_SIZE);
if (!iobase) {
dev_err(dev, "unable to map iobase\n");
return -ENODEV;
}
/* check version? */
if (readl(iobase + R104_VERSION) != 0x4000) {
dev_dbg(dev, "no supported mmc controller found\n");
result = -ENODEV;
goto err;
}
/* allocate privdata */
mmc = pcmcia_dev->priv =
mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev);
if (!mmc) {
dev_err(dev, "mmc_alloc_host failed\n");
result = -ENOMEM;
goto err;
}
host = mmc_priv(mmc);
host->iobase = iobase;
host->dev = dev;
host->pci_dev = pci_dev;
mmc->ops = &sdricoh_ops;
/* FIXME: frequency and voltage handling is done by the controller
*/
mmc->f_min = 450000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->max_seg_size = 1024 * 512;
mmc->max_blk_size = 512;
/* reset the controler */
if (sdricoh_reset(host)) {
dev_dbg(dev, "could not reset\n");
result = -EIO;
goto err;
}
result = mmc_add_host(mmc);
if (!result) {
dev_dbg(dev, "mmc host registered\n");
return 0;
}
err:
if (iobase)
iounmap(iobase);
if (mmc)
mmc_free_host(mmc);
return result;
}
/* search for supported mmc controllers */
static int sdricoh_pcmcia_probe(struct pcmcia_device *pcmcia_dev)
{
struct pci_dev *pci_dev = NULL;
dev_info(&pcmcia_dev->dev, "Searching MMC controller for pcmcia device"
" %s %s ...\n", pcmcia_dev->prod_id[0], pcmcia_dev->prod_id[1]);
/* search pci cardbus bridge that contains the mmc controler */
/* the io region is already claimed by yenta_socket... */
while ((pci_dev =
pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476,
pci_dev))) {
/* try to init the device */
if (!sdricoh_init_mmc(pci_dev, pcmcia_dev)) {
dev_info(&pcmcia_dev->dev, "MMC controller found\n");
return 0;
}
}
dev_err(&pcmcia_dev->dev, "No MMC controller was found.\n");
return -ENODEV;
}
static void sdricoh_pcmcia_detach(struct pcmcia_device *link)
{
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "detach\n");
/* remove mmc host */
if (mmc) {
struct sdricoh_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
pci_iounmap(host->pci_dev, host->iobase);
pci_dev_put(host->pci_dev);
mmc_free_host(mmc);
}
pcmcia_disable_device(link);
}
#ifdef CONFIG_PM
static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
{
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "suspend\n");
mmc_suspend_host(mmc, PMSG_SUSPEND);
return 0;
}
static int sdricoh_pcmcia_resume(struct pcmcia_device *link)
{
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "resume\n");
sdricoh_reset(mmc_priv(mmc));
mmc_resume_host(mmc);
return 0;
}
#else
#define sdricoh_pcmcia_suspend NULL
#define sdricoh_pcmcia_resume NULL
#endif
static struct pcmcia_driver sdricoh_driver = {
.drv = {
.name = DRIVER_NAME,
},
.probe = sdricoh_pcmcia_probe,
.remove = sdricoh_pcmcia_detach,
.id_table = pcmcia_ids,
.suspend = sdricoh_pcmcia_suspend,
.resume = sdricoh_pcmcia_resume,
};
/*****************************************************************************\
* *
* Driver init/exit *
* *
\*****************************************************************************/
static int __init sdricoh_drv_init(void)
{
return pcmcia_register_driver(&sdricoh_driver);
}
static void __exit sdricoh_drv_exit(void)
{
pcmcia_unregister_driver(&sdricoh_driver);
}
module_init(sdricoh_drv_init);
module_exit(sdricoh_drv_exit);
module_param(switchlocked, uint, 0444);
MODULE_AUTHOR("Sascha Sommer <saschasommer@freenet.de>");
MODULE_DESCRIPTION("Ricoh PCMCIA Secure Digital Interface driver");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(switchlocked, "Switch the cards locked status."
"Use this when unlocked cards are shown readonly (default 0)");

View File

@ -973,7 +973,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
mmc->ops = &tifm_sd_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
mmc->caps = MMC_CAP_4_BIT_DATA;
mmc->f_min = 20000000 / 60;
mmc->f_max = 24000000;

View File

@ -68,16 +68,16 @@ static const int unlock_codes[] = { 0x83, 0x87 };
static const int valid_ids[] = {
0x7112,
};
};
#ifdef CONFIG_PNP
static unsigned int nopnp = 0;
static unsigned int param_nopnp = 0;
#else
static const unsigned int nopnp = 1;
static const unsigned int param_nopnp = 1;
#endif
static unsigned int io = 0x248;
static unsigned int irq = 6;
static int dma = 2;
static unsigned int param_io = 0x248;
static unsigned int param_irq = 6;
static int param_dma = 2;
/*
* Basic functions
@ -939,7 +939,7 @@ static int wbsd_get_ro(struct mmc_host *mmc)
spin_unlock_bh(&host->lock);
return csr & WBSD_WRPT;
return !!(csr & WBSD_WRPT);
}
static const struct mmc_host_ops wbsd_ops = {
@ -1219,7 +1219,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
mmc->f_min = 375000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
mmc->caps = MMC_CAP_4_BIT_DATA;
spin_lock_init(&host->lock);
@ -1420,7 +1420,7 @@ kfree:
dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
host->dma_addr = (dma_addr_t)NULL;
host->dma_addr = 0;
kfree(host->dma_buffer);
host->dma_buffer = NULL;
@ -1445,7 +1445,7 @@ static void wbsd_release_dma(struct wbsd_host *host)
host->dma = -1;
host->dma_buffer = NULL;
host->dma_addr = (dma_addr_t)NULL;
host->dma_addr = 0;
}
/*
@ -1765,7 +1765,7 @@ static void __devexit wbsd_shutdown(struct device *dev, int pnp)
static int __devinit wbsd_probe(struct platform_device *dev)
{
/* Use the module parameters for resources */
return wbsd_init(&dev->dev, io, irq, dma, 0);
return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0);
}
static int __devexit wbsd_remove(struct platform_device *dev)
@ -1979,14 +1979,14 @@ static int __init wbsd_drv_init(void)
#ifdef CONFIG_PNP
if (!nopnp) {
if (!param_nopnp) {
result = pnp_register_driver(&wbsd_pnp_driver);
if (result < 0)
return result;
}
#endif /* CONFIG_PNP */
if (nopnp) {
if (param_nopnp) {
result = platform_driver_register(&wbsd_driver);
if (result < 0)
return result;
@ -2012,12 +2012,12 @@ static void __exit wbsd_drv_exit(void)
{
#ifdef CONFIG_PNP
if (!nopnp)
if (!param_nopnp)
pnp_unregister_driver(&wbsd_pnp_driver);
#endif /* CONFIG_PNP */
if (nopnp) {
if (param_nopnp) {
platform_device_unregister(wbsd_device);
platform_driver_unregister(&wbsd_driver);
@ -2029,11 +2029,11 @@ static void __exit wbsd_drv_exit(void)
module_init(wbsd_drv_init);
module_exit(wbsd_drv_exit);
#ifdef CONFIG_PNP
module_param(nopnp, uint, 0444);
module_param_named(nopnp, param_nopnp, uint, 0444);
#endif
module_param(io, uint, 0444);
module_param(irq, uint, 0444);
module_param(dma, int, 0444);
module_param_named(io, param_io, uint, 0444);
module_param_named(irq, param_irq, uint, 0444);
module_param_named(dma, param_dma, int, 0444);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");

View File

@ -1,7 +1,7 @@
/*
* linux/drivers/net/wireless/libertas/if_sdio.c
*
* Copyright 2007 Pierre Ossman
* Copyright 2007-2008 Pierre Ossman
*
* Inspired by if_cs.c, Copyright 2007 Holger Schurig
*
@ -266,13 +266,10 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)
/*
* The transfer must be in one transaction or the firmware
* goes suicidal.
* goes suicidal. There's no way to guarantee that for all
* controllers, but we can at least try.
*/
chunk = size;
if ((chunk > card->func->cur_blksize) || (chunk > 512)) {
chunk = (chunk + card->func->cur_blksize - 1) /
card->func->cur_blksize * card->func->cur_blksize;
}
chunk = sdio_align_size(card->func, size);
ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
if (ret)
@ -696,13 +693,10 @@ static int if_sdio_host_to_card(struct lbs_private *priv,
/*
* The transfer must be in one transaction or the firmware
* goes suicidal.
* goes suicidal. There's no way to guarantee that for all
* controllers, but we can at least try.
*/
size = nb + 4;
if ((size > card->func->cur_blksize) || (size > 512)) {
size = (size + card->func->cur_blksize - 1) /
card->func->cur_blksize * card->func->cur_blksize;
}
size = sdio_align_size(card->func, nb + 4);
packet = kzalloc(sizeof(struct if_sdio_packet) + size,
GFP_ATOMIC);

View File

@ -75,6 +75,10 @@
#define AT91_MCI_TRTYP_MULTIPLE (1 << 19)
#define AT91_MCI_TRTYP_STREAM (2 << 19)
#define AT91_MCI_BLKR 0x18 /* Block Register */
#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */
#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block lenght */
#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */
#define AT91_MCR_RDR 0x30 /* Receive Data Register */
#define AT91_MCR_TDR 0x34 /* Transmit Data Register */

View File

@ -28,9 +28,15 @@
#define S3C2410_SDIDCNT (0x30)
#define S3C2410_SDIDSTA (0x34)
#define S3C2410_SDIFSTA (0x38)
#define S3C2410_SDIDATA (0x3C)
#define S3C2410_SDIIMSK (0x40)
#define S3C2440_SDIDATA (0x40)
#define S3C2440_SDIIMSK (0x3C)
#define S3C2440_SDICON_SDRESET (1<<8)
#define S3C2440_SDICON_MMCCLOCK (1<<5)
#define S3C2410_SDICON_BYTEORDER (1<<4)
#define S3C2410_SDICON_SDIOIRQ (1<<3)
#define S3C2410_SDICON_RWAITEN (1<<2)
@ -42,7 +48,8 @@
#define S3C2410_SDICMDCON_LONGRSP (1<<10)
#define S3C2410_SDICMDCON_WAITRSP (1<<9)
#define S3C2410_SDICMDCON_CMDSTART (1<<8)
#define S3C2410_SDICMDCON_INDEX (0xff)
#define S3C2410_SDICMDCON_SENDERHOST (1<<6)
#define S3C2410_SDICMDCON_INDEX (0x3f)
#define S3C2410_SDICMDSTAT_CRCFAIL (1<<12)
#define S3C2410_SDICMDSTAT_CMDSENT (1<<11)
@ -51,6 +58,9 @@
#define S3C2410_SDICMDSTAT_XFERING (1<<8)
#define S3C2410_SDICMDSTAT_INDEX (0xff)
#define S3C2440_SDIDCON_DS_BYTE (0<<22)
#define S3C2440_SDIDCON_DS_HALFWORD (1<<22)
#define S3C2440_SDIDCON_DS_WORD (2<<22)
#define S3C2410_SDIDCON_IRQPERIOD (1<<21)
#define S3C2410_SDIDCON_TXAFTERRESP (1<<20)
#define S3C2410_SDIDCON_RXAFTERCMD (1<<19)
@ -59,6 +69,7 @@
#define S3C2410_SDIDCON_WIDEBUS (1<<16)
#define S3C2410_SDIDCON_DMAEN (1<<15)
#define S3C2410_SDIDCON_STOP (1<<14)
#define S3C2440_SDIDCON_DATSTART (1<<14)
#define S3C2410_SDIDCON_DATMODE (3<<12)
#define S3C2410_SDIDCON_BLKNUM (0x7ff)
@ -68,6 +79,7 @@
#define S3C2410_SDIDCON_XFER_RXSTART (2<<12)
#define S3C2410_SDIDCON_XFER_TXSTART (3<<12)
#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF)
#define S3C2410_SDIDCNT_BLKNUM_SHIFT (12)
#define S3C2410_SDIDSTA_RDYWAITREQ (1<<10)
@ -82,10 +94,12 @@
#define S3C2410_SDIDSTA_TXDATAON (1<<1)
#define S3C2410_SDIDSTA_RXDATAON (1<<0)
#define S3C2440_SDIFSTA_FIFORESET (1<<16)
#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
#define S3C2410_SDIFSTA_TFDET (1<<13)
#define S3C2410_SDIFSTA_RFDET (1<<12)
#define S3C2410_SDIFSTA_TXHALF (1<<11)
#define S3C2410_SDIFSTA_TXEMPTY (1<<10)
#define S3C2410_SDIFSTA_TFHALF (1<<11)
#define S3C2410_SDIFSTA_TFEMPTY (1<<10)
#define S3C2410_SDIFSTA_RFLAST (1<<9)
#define S3C2410_SDIFSTA_RFFULL (1<<8)
#define S3C2410_SDIFSTA_RFHALF (1<<7)

View File

@ -0,0 +1,15 @@
#ifndef _ARCH_MCI_H
#define _ARCH_MCI_H
struct s3c24xx_mci_pdata {
unsigned int wprotect_invert : 1;
unsigned int detect_invert : 1; /* set => detect active high. */
unsigned int gpio_detect;
unsigned int gpio_wprotect;
unsigned long ocr_avail;
void (*set_power)(unsigned char power_mode,
unsigned short vdd);
};
#endif /* _ARCH_NCI_H */

View File

@ -77,7 +77,11 @@ struct i2c_board_info;
struct platform_device *at32_add_device_twi(unsigned int id,
struct i2c_board_info *b,
unsigned int n);
struct platform_device *at32_add_device_mci(unsigned int id);
struct mci_platform_data;
struct platform_device *
at32_add_device_mci(unsigned int id, struct mci_platform_data *data);
struct platform_device *at32_add_device_ac97c(unsigned int id);
struct platform_device *at32_add_device_abdac(unsigned int id);
struct platform_device *at32_add_device_psif(unsigned int id);

View File

@ -0,0 +1,9 @@
#ifndef __ASM_AVR32_ATMEL_MCI_H
#define __ASM_AVR32_ATMEL_MCI_H
struct mci_platform_data {
int detect_pin;
int wp_pin;
};
#endif /* __ASM_AVR32_ATMEL_MCI_H */

View File

@ -38,15 +38,15 @@
#ifndef __ASM_AU1100_MMC_H
#define __ASM_AU1100_MMC_H
#include <linux/leds.h>
#define NUM_AU1100_MMC_CONTROLLERS 2
#if defined(CONFIG_SOC_AU1100)
#define AU1100_SD_IRQ AU1100_SD_INT
#elif defined(CONFIG_SOC_AU1200)
#define AU1100_SD_IRQ AU1200_SD_INT
#endif
struct au1xmmc_platform_data {
int(*cd_setup)(void *mmc_host, int on);
int(*card_inserted)(void *mmc_host);
int(*card_readonly)(void *mmc_host);
void(*set_power)(void *mmc_host, int state);
struct led_classdev *led;
};
#define SD0_BASE 0xB0600000
#define SD1_BASE 0xB0680000

View File

@ -135,6 +135,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host);

View File

@ -51,8 +51,30 @@ struct mmc_ios {
struct mmc_host_ops {
void (*request)(struct mmc_host *host, struct mmc_request *req);
/*
* Avoid calling these three functions too often or in a "fast path",
* since underlaying controller might implement them in an expensive
* and/or slow way.
*
* Also note that these functions might sleep, so don't call them
* in the atomic contexts!
*
* Return values for the get_ro callback should be:
* 0 for a read/write card
* 1 for a read-only card
* -ENOSYS when not supported (equal to NULL callback)
* or a negative errno value when something bad happened
*
* Return values for the get_ro callback should be:
* 0 for a absent card
* 1 for a present card
* -ENOSYS when not supported (equal to NULL callback)
* or a negative errno value when something bad happened
*/
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *host);
int (*get_cd)(struct mmc_host *host);
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
};
@ -89,11 +111,11 @@ struct mmc_host {
unsigned long caps; /* Host capabilities */
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
#define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */
#define MMC_CAP_MMC_HIGHSPEED (1 << 2) /* Can do MMC high-speed timing */
#define MMC_CAP_SD_HIGHSPEED (1 << 3) /* Can do SD high-speed timing */
#define MMC_CAP_SDIO_IRQ (1 << 4) /* Can signal pending SDIO IRQs */
#define MMC_CAP_SPI (1 << 5) /* Talks only SPI protocols */
#define MMC_CAP_MMC_HIGHSPEED (1 << 1) /* Can do MMC high-speed timing */
#define MMC_CAP_SD_HIGHSPEED (1 << 2) /* Can do SD high-speed timing */
#define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */

View File

@ -16,7 +16,6 @@
* Based strongly on code by:
*
* Author: Yong-iL Joh <tolkien@mizi.com>
* Date : $Date: 2002/06/18 12:37:30 $
*
* Author: Andrew Christian
* 15 May 2002

View File

@ -1,7 +1,7 @@
/*
* include/linux/mmc/sdio_func.h
*
* Copyright 2007 Pierre Ossman
* Copyright 2007-2008 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -46,6 +46,8 @@ struct sdio_func {
unsigned max_blksize; /* maximum block size */
unsigned cur_blksize; /* current block size */
unsigned enable_timeout; /* max enable timeout in msec */
unsigned int state; /* function state */
#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */
@ -120,23 +122,22 @@ extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
extern int sdio_release_irq(struct sdio_func *func);
extern unsigned char sdio_readb(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern unsigned short sdio_readw(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern unsigned long sdio_readl(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz);
extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret);
extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret);
extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret);
extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
unsigned int addr, int count);
extern int sdio_readsb(struct sdio_func *func, void *dst,
unsigned int addr, int count);
extern void sdio_writeb(struct sdio_func *func, unsigned char b,
extern void sdio_writeb(struct sdio_func *func, u8 b,
unsigned int addr, int *err_ret);
extern void sdio_writew(struct sdio_func *func, unsigned short b,
extern void sdio_writew(struct sdio_func *func, u16 b,
unsigned int addr, int *err_ret);
extern void sdio_writel(struct sdio_func *func, unsigned long b,
extern void sdio_writel(struct sdio_func *func, u32 b,
unsigned int addr, int *err_ret);
extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,

View File

@ -2190,6 +2190,7 @@
#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366
#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368
#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381
#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382
#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383
#define PCI_VENDOR_ID_KORENIX 0x1982

View File

@ -23,6 +23,15 @@ struct mmc_spi_platform_data {
/* sense switch on sd cards */
int (*get_ro)(struct device *);
/*
* If board does not use CD interrupts, driver can optimize polling
* using this function.
*/
int (*get_cd)(struct device *);
/* Capabilities to pass into mmc core (e.g. MMC_CAP_NEEDS_POLL). */
unsigned long caps;
/* how long to debounce card detect, in msecs */
u16 detect_delay;