You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge 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:
+16
-1
@@ -348,7 +348,9 @@ W: http://www.linux-usb.org/SpeedTouch/
|
|||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
||||||
ALCHEMY AU1XX0 MMC DRIVER
|
ALCHEMY AU1XX0 MMC DRIVER
|
||||||
S: Orphan
|
P: Manuel Lauss
|
||||||
|
M: manuel.lauss@gmail.com
|
||||||
|
S: Maintained
|
||||||
|
|
||||||
ALI1563 I2C DRIVER
|
ALI1563 I2C DRIVER
|
||||||
P: Rudolf Marek
|
P: Rudolf Marek
|
||||||
@@ -3559,6 +3561,13 @@ L: linux-s390@vger.kernel.org
|
|||||||
W: http://www.ibm.com/developerworks/linux/linux390/
|
W: http://www.ibm.com/developerworks/linux/linux390/
|
||||||
S: Supported
|
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
|
SAA7146 VIDEO4LINUX-2 DRIVER
|
||||||
P: Michael Hunold
|
P: Michael Hunold
|
||||||
M: michael@mihu.de
|
M: michael@mihu.de
|
||||||
@@ -3631,6 +3640,12 @@ P: Jim Cromie
|
|||||||
M: jim.cromie@gmail.com
|
M: jim.cromie@gmail.com
|
||||||
S: Maintained
|
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
|
SECURITY CONTACT
|
||||||
P: Security Officers
|
P: Security Officers
|
||||||
M: security@kernel.org
|
M: security@kernel.org
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#include <asm/atmel-mci.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/setup.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
|
* The next two functions should go away as the boot loader is
|
||||||
* supposed to initialize the macb address registers with a valid
|
* 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, ð_data[1]));
|
set_hw_addr(at32_add_device_eth(1, ð_data[1]));
|
||||||
|
|
||||||
at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info));
|
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);
|
at32_add_device_usba(0, NULL);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ngw_leds); i++) {
|
for (i = 0; i < ARRAY_SIZE(ngw_leds); i++) {
|
||||||
|
|||||||
@@ -234,6 +234,9 @@ static int __init atstk1002_init(void)
|
|||||||
#ifdef CONFIG_BOARD_ATSTK100X_SPI1
|
#ifdef CONFIG_BOARD_ATSTK100X_SPI1
|
||||||
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
|
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM
|
||||||
|
at32_add_device_mci(0, NULL);
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM
|
#ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM
|
||||||
set_hw_addr(at32_add_device_eth(1, ð_data[1]));
|
set_hw_addr(at32_add_device_eth(1, ð_data[1]));
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/usb/atmel_usba_udc.h>
|
#include <linux/usb/atmel_usba_udc.h>
|
||||||
|
|
||||||
|
#include <asm/atmel-mci.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
|
||||||
@@ -1278,20 +1279,32 @@ static struct clk atmel_mci0_pclk = {
|
|||||||
.index = 9,
|
.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)
|
if (id != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
pdev = platform_device_alloc("atmel_mci", id);
|
pdev = platform_device_alloc("atmel_mci", id);
|
||||||
if (!pdev)
|
if (!pdev)
|
||||||
return NULL;
|
goto fail;
|
||||||
|
|
||||||
if (platform_device_add_resources(pdev, atmel_mci0_resource,
|
if (platform_device_add_resources(pdev, atmel_mci0_resource,
|
||||||
ARRAY_SIZE(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(10), PERIPH_A, 0); /* CLK */
|
||||||
select_peripheral(PA(11), PERIPH_A, 0); /* CMD */
|
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(14), PERIPH_A, 0); /* DATA2 */
|
||||||
select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */
|
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;
|
atmel_mci0_pclk.dev = &pdev->dev;
|
||||||
|
|
||||||
platform_device_add(pdev);
|
platform_device_add(pdev);
|
||||||
return pdev;
|
return pdev;
|
||||||
|
|
||||||
err_add_resources:
|
fail:
|
||||||
platform_device_put(pdev);
|
platform_device_put(pdev);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-31
@@ -2,7 +2,7 @@
|
|||||||
* Block driver for media (i.e., flash cards)
|
* Block driver for media (i.e., flash cards)
|
||||||
*
|
*
|
||||||
* Copyright 2002 Hewlett-Packard Company
|
* Copyright 2002 Hewlett-Packard Company
|
||||||
* Copyright 2005-2007 Pierre Ossman
|
* Copyright 2005-2008 Pierre Ossman
|
||||||
*
|
*
|
||||||
* Use consistent with the GNU GPL is permitted,
|
* Use consistent with the GNU GPL is permitted,
|
||||||
* provided that this copyright notice is
|
* 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)
|
if (brq.data.blocks > card->host->max_blk_count)
|
||||||
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) {
|
if (brq.data.blocks > 1) {
|
||||||
/* SPI multiblock writes terminate using a special
|
/* SPI multiblock writes terminate using a special
|
||||||
* token, not a STOP_TRANSMISSION request.
|
* 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);
|
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) {
|
if (brq.cmd.error) {
|
||||||
printk(KERN_ERR "%s: error %d sending read/write command\n",
|
printk(KERN_ERR "%s: error %d sending read/write command\n",
|
||||||
req->rq_disk->disk_name, brq.cmd.error);
|
req->rq_disk->disk_name, brq.cmd.error);
|
||||||
goto cmd_err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (brq.data.error) {
|
if (brq.data.error) {
|
||||||
printk(KERN_ERR "%s: error %d transferring data\n",
|
printk(KERN_ERR "%s: error %d transferring data\n",
|
||||||
req->rq_disk->disk_name, brq.data.error);
|
req->rq_disk->disk_name, brq.data.error);
|
||||||
goto cmd_err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (brq.stop.error) {
|
if (brq.stop.error) {
|
||||||
printk(KERN_ERR "%s: error %d sending stop command\n",
|
printk(KERN_ERR "%s: error %d sending stop command\n",
|
||||||
req->rq_disk->disk_name, brq.stop.error);
|
req->rq_disk->disk_name, brq.stop.error);
|
||||||
goto cmd_err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (brq.cmd.error || brq.data.error || brq.stop.error)
|
||||||
|
goto cmd_err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A block was successfully transferred.
|
* 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.
|
* mark the known good sectors as ok.
|
||||||
*
|
*
|
||||||
* If the card is not SD, we can still ok written sectors
|
* 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
|
* For reads we just fail the entire chunk as that should
|
||||||
* be safe in all cases.
|
* be safe in all cases.
|
||||||
*/
|
*/
|
||||||
if (rq_data_dir(req) != READ && mmc_card_sd(card)) {
|
if (rq_data_dir(req) != READ) {
|
||||||
u32 blocks;
|
if (mmc_card_sd(card)) {
|
||||||
unsigned int bytes;
|
u32 blocks;
|
||||||
|
unsigned int bytes;
|
||||||
|
|
||||||
blocks = mmc_sd_num_wr_blocks(card);
|
blocks = mmc_sd_num_wr_blocks(card);
|
||||||
if (blocks != (u32)-1) {
|
if (blocks != (u32)-1) {
|
||||||
if (card->csd.write_partial)
|
if (card->csd.write_partial)
|
||||||
bytes = blocks << md->block_bits;
|
bytes = blocks << md->block_bits;
|
||||||
else
|
else
|
||||||
bytes = blocks << 9;
|
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);
|
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);
|
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);
|
mmc_release_host(card->host);
|
||||||
|
|||||||
+383
-200
File diff suppressed because it is too large
Load Diff
@@ -885,12 +885,14 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t
|
|||||||
sdio_uart_release_func(port);
|
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;
|
struct sdio_uart_port *port = tty->driver_data;
|
||||||
|
int result;
|
||||||
|
|
||||||
if (sdio_uart_claim_func(port) != 0)
|
result = sdio_uart_claim_func(port);
|
||||||
return;
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
if (break_state == -1)
|
if (break_state == -1)
|
||||||
port->lcr |= UART_LCR_SBC;
|
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_out(port, UART_LCR, port->lcr);
|
||||||
|
|
||||||
sdio_uart_release_func(port);
|
sdio_uart_release_func(port);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
|
static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
|
||||||
|
|||||||
+37
-4
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
|
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
|
||||||
* SD support Copyright (C) 2004 Ian Molton, 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.
|
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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);
|
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
|
* __mmc_claim_host - exclusively claim a host
|
||||||
* @host: mmc host to claim
|
* @host: mmc host to claim
|
||||||
@@ -638,6 +665,9 @@ void mmc_rescan(struct work_struct *work)
|
|||||||
*/
|
*/
|
||||||
mmc_bus_put(host);
|
mmc_bus_put(host);
|
||||||
|
|
||||||
|
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
|
|
||||||
mmc_power_up(host);
|
mmc_power_up(host);
|
||||||
@@ -652,7 +682,7 @@ void mmc_rescan(struct work_struct *work)
|
|||||||
if (!err) {
|
if (!err) {
|
||||||
if (mmc_attach_sdio(host, ocr))
|
if (mmc_attach_sdio(host, ocr))
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -662,7 +692,7 @@ void mmc_rescan(struct work_struct *work)
|
|||||||
if (!err) {
|
if (!err) {
|
||||||
if (mmc_attach_sd(host, ocr))
|
if (mmc_attach_sd(host, ocr))
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -672,7 +702,7 @@ void mmc_rescan(struct work_struct *work)
|
|||||||
if (!err) {
|
if (!err) {
|
||||||
if (mmc_attach_mmc(host, ocr))
|
if (mmc_attach_mmc(host, ocr))
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
@@ -683,6 +713,9 @@ void mmc_rescan(struct work_struct *work)
|
|||||||
|
|
||||||
mmc_bus_put(host);
|
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)
|
void mmc_start_host(struct mmc_host *host)
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ static struct device_type mmc_type = {
|
|||||||
/*
|
/*
|
||||||
* Handle the detection and initialisation of a card.
|
* 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.
|
* we're trying to reinitialise.
|
||||||
*/
|
*/
|
||||||
static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ static struct device_type sd_type = {
|
|||||||
/*
|
/*
|
||||||
* Handle the detection and initialisation of a card.
|
* 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.
|
* we're trying to reinitialise.
|
||||||
*/
|
*/
|
||||||
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
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.
|
* Check if read-only switch is active.
|
||||||
*/
|
*/
|
||||||
if (!oldcard) {
|
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 "
|
printk(KERN_WARNING "%s: host does not "
|
||||||
"support reading read-only "
|
"support reading read-only "
|
||||||
"switch. assuming write-enable.\n",
|
"switch. assuming write-enable.\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
} else {
|
} else {
|
||||||
if (host->ops->get_ro(host))
|
if (host->ops->get_ro(host) > 0)
|
||||||
mmc_card_set_readonly(card);
|
mmc_card_set_readonly(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,12 @@ static int cistpl_funce_func(struct sdio_func *func,
|
|||||||
/* TPLFE_MAX_BLK_SIZE */
|
/* TPLFE_MAX_BLK_SIZE */
|
||||||
func->max_blksize = buf[12] | (buf[13] << 8);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+128
-39
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* linux/drivers/mmc/core/sdio_io.c
|
* 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
|
* 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
|
* 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)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/*
|
timeout = jiffies + msecs_to_jiffies(func->enable_timeout);
|
||||||
* FIXME: This should timeout based on information in the CIS,
|
|
||||||
* but we don't have card to parse that yet.
|
|
||||||
*/
|
|
||||||
timeout = jiffies + HZ;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®);
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®);
|
||||||
@@ -167,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (blksz == 0) {
|
if (blksz == 0) {
|
||||||
blksz = min(min(
|
blksz = min(func->max_blksize, func->card->host->max_blk_size);
|
||||||
func->max_blksize,
|
blksz = min(blksz, 512u);
|
||||||
func->card->host->max_blk_size),
|
|
||||||
512u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mmc_io_rw_direct(func->card, 1, 0,
|
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;
|
func->cur_blksize = blksz;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(sdio_set_block_size);
|
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
|
/* Split an arbitrarily sized data transfer into several
|
||||||
* IO_RW_EXTENDED commands. */
|
* IO_RW_EXTENDED commands. */
|
||||||
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|
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;
|
int ret;
|
||||||
|
|
||||||
/* Do the bulk of the transfer using block mode (if supported). */
|
/* 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
|
/* Blocks per command is limited by host count, host transfer
|
||||||
* size (we only use a single sg entry) and the maximum for
|
* size (we only use a single sg entry) and the maximum for
|
||||||
* IO_RW_EXTENDED of 511 blocks. */
|
* IO_RW_EXTENDED of 511 blocks. */
|
||||||
max_blocks = min(min(
|
max_blocks = min(func->card->host->max_blk_count,
|
||||||
func->card->host->max_blk_count,
|
func->card->host->max_seg_size / func->cur_blksize);
|
||||||
func->card->host->max_seg_size / func->cur_blksize),
|
max_blocks = min(max_blocks, 511u);
|
||||||
511u);
|
|
||||||
|
|
||||||
while (remainder > func->cur_blksize) {
|
while (remainder > func->cur_blksize) {
|
||||||
unsigned blocks;
|
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. */
|
/* Write the remainder using byte mode. */
|
||||||
while (remainder > 0) {
|
while (remainder > 0) {
|
||||||
size = remainder;
|
size = min(remainder, sdio_max_byte_size(func));
|
||||||
if (size > func->cur_blksize)
|
|
||||||
size = func->cur_blksize;
|
|
||||||
if (size > 512)
|
|
||||||
size = 512; /* maximum size for byte mode */
|
|
||||||
|
|
||||||
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
|
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
|
||||||
incr_addr, buf, 1, size);
|
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
|
* function. If there is a problem reading the address, 0xff
|
||||||
* is returned and @err_ret will contain the error code.
|
* is returned and @err_ret will contain the error code.
|
||||||
*/
|
*/
|
||||||
unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
|
u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
|
||||||
int *err_ret)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned char val;
|
u8 val;
|
||||||
|
|
||||||
BUG_ON(!func);
|
BUG_ON(!func);
|
||||||
|
|
||||||
@@ -293,8 +388,7 @@ EXPORT_SYMBOL_GPL(sdio_readb);
|
|||||||
* function. @err_ret will contain the status of the actual
|
* function. @err_ret will contain the status of the actual
|
||||||
* transfer.
|
* transfer.
|
||||||
*/
|
*/
|
||||||
void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
|
void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)
|
||||||
int *err_ret)
|
|
||||||
{
|
{
|
||||||
int 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);
|
return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(sdio_readsb);
|
EXPORT_SYMBOL_GPL(sdio_readsb);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -385,8 +478,7 @@ EXPORT_SYMBOL_GPL(sdio_writesb);
|
|||||||
* function. If there is a problem reading the address, 0xffff
|
* function. If there is a problem reading the address, 0xffff
|
||||||
* is returned and @err_ret will contain the error code.
|
* is returned and @err_ret will contain the error code.
|
||||||
*/
|
*/
|
||||||
unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
|
u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret)
|
||||||
int *err_ret)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -400,7 +492,7 @@ unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
|
|||||||
return 0xFFFF;
|
return 0xFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return le16_to_cpu(*(u16*)func->tmpbuf);
|
return le16_to_cpup((__le16 *)func->tmpbuf);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdio_readw);
|
EXPORT_SYMBOL_GPL(sdio_readw);
|
||||||
|
|
||||||
@@ -415,12 +507,11 @@ EXPORT_SYMBOL_GPL(sdio_readw);
|
|||||||
* function. @err_ret will contain the status of the actual
|
* function. @err_ret will contain the status of the actual
|
||||||
* transfer.
|
* transfer.
|
||||||
*/
|
*/
|
||||||
void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
|
void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret)
|
||||||
int *err_ret)
|
|
||||||
{
|
{
|
||||||
int 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);
|
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
|
||||||
if (err_ret)
|
if (err_ret)
|
||||||
@@ -439,8 +530,7 @@ EXPORT_SYMBOL_GPL(sdio_writew);
|
|||||||
* 0xffffffff is returned and @err_ret will contain the error
|
* 0xffffffff is returned and @err_ret will contain the error
|
||||||
* code.
|
* code.
|
||||||
*/
|
*/
|
||||||
unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
|
u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret)
|
||||||
int *err_ret)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -454,7 +544,7 @@ unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
|
|||||||
return 0xFFFFFFFF;
|
return 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return le32_to_cpu(*(u32*)func->tmpbuf);
|
return le32_to_cpup((__le32 *)func->tmpbuf);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdio_readl);
|
EXPORT_SYMBOL_GPL(sdio_readl);
|
||||||
|
|
||||||
@@ -469,12 +559,11 @@ EXPORT_SYMBOL_GPL(sdio_readl);
|
|||||||
* function. @err_ret will contain the status of the actual
|
* function. @err_ret will contain the status of the actual
|
||||||
* transfer.
|
* transfer.
|
||||||
*/
|
*/
|
||||||
void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
|
void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret)
|
||||||
int *err_ret)
|
|
||||||
{
|
{
|
||||||
int 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);
|
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
|
||||||
if (err_ret)
|
if (err_ret)
|
||||||
|
|||||||
@@ -26,18 +26,31 @@ config MMC_PXA
|
|||||||
|
|
||||||
config MMC_SDHCI
|
config MMC_SDHCI
|
||||||
tristate "Secure Digital Host Controller Interface support"
|
tristate "Secure Digital Host Controller Interface support"
|
||||||
depends on PCI
|
depends on HAS_DMA
|
||||||
help
|
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)
|
It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
|
||||||
and Toshiba(R). Most controllers found in laptops are of this type.
|
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 you have a controller with this interface, say Y or M here.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
config MMC_RICOH_MMC
|
config MMC_RICOH_MMC
|
||||||
tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
|
tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
|
||||||
depends on PCI && EXPERIMENTAL && MMC_SDHCI
|
depends on MMC_SDHCI_PCI
|
||||||
help
|
help
|
||||||
This selects the disabler for the Ricoh MMC Controller. This
|
This selects the disabler for the Ricoh MMC Controller. This
|
||||||
proprietary controller is unnecessary because the SDHCI driver
|
proprietary controller is unnecessary because the SDHCI driver
|
||||||
@@ -91,6 +104,16 @@ config MMC_AT91
|
|||||||
|
|
||||||
If unsure, say N.
|
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
|
config MMC_IMX
|
||||||
tristate "Motorola i.MX Multimedia Card Interface support"
|
tristate "Motorola i.MX Multimedia Card Interface support"
|
||||||
depends on ARCH_IMX
|
depends on ARCH_IMX
|
||||||
@@ -130,3 +153,24 @@ config MMC_SPI
|
|||||||
|
|
||||||
If unsure, or if your system has no SPI master driver, say N.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,15 @@ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
|
|||||||
obj-$(CONFIG_MMC_PXA) += pxamci.o
|
obj-$(CONFIG_MMC_PXA) += pxamci.o
|
||||||
obj-$(CONFIG_MMC_IMX) += imxmmc.o
|
obj-$(CONFIG_MMC_IMX) += imxmmc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI) += sdhci.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_RICOH_MMC) += ricoh_mmc.o
|
||||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||||
obj-$(CONFIG_MMC_OMAP) += omap.o
|
obj-$(CONFIG_MMC_OMAP) += omap.o
|
||||||
obj-$(CONFIG_MMC_AT91) += at91_mci.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_TIFM_SD) += tifm_sd.o
|
||||||
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
|
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
|
||||||
|
obj-$(CONFIG_MMC_S3C) += s3cmci.o
|
||||||
|
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
|
||||||
|
|
||||||
|
|||||||
+208
-51
@@ -125,8 +125,71 @@ struct at91mci_host
|
|||||||
|
|
||||||
/* Latest in the scatterlist that has been enabled for transfer */
|
/* Latest in the scatterlist that has been enabled for transfer */
|
||||||
int transfer_index;
|
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
|
* 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 int len, i, size;
|
||||||
unsigned *dmabuf = host->buffer;
|
unsigned *dmabuf = host->buffer;
|
||||||
|
|
||||||
size = host->total_length;
|
size = data->blksz * data->blocks;
|
||||||
len = data->sg_len;
|
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
|
* Just loop through all entries. Size might not
|
||||||
* be the entire list though so make sure that
|
* 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++)
|
for (index = 0; index < (amount / 4); index++)
|
||||||
*dmabuf++ = swab32(sgbuffer[index]);
|
*dmabuf++ = swab32(sgbuffer[index]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
memcpy(dmabuf, sgbuffer, amount);
|
memcpy(dmabuf, sgbuffer, amount);
|
||||||
|
dmabuf += amount;
|
||||||
|
}
|
||||||
|
|
||||||
kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
|
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) {
|
if (i == 0) {
|
||||||
at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
|
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 {
|
else {
|
||||||
at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
|
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);
|
dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
|
||||||
|
|
||||||
data->bytes_xfered += sg->length;
|
|
||||||
|
|
||||||
if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
|
if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
|
||||||
unsigned int *buffer;
|
unsigned int *buffer;
|
||||||
int index;
|
int index;
|
||||||
@@ -294,6 +361,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
|
|||||||
}
|
}
|
||||||
|
|
||||||
flush_dcache_page(sg_page(sg));
|
flush_dcache_page(sg_page(sg));
|
||||||
|
|
||||||
|
data->bytes_xfered += sg->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is there another transfer to trigger? */
|
/* 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);
|
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
|
||||||
} else
|
} else
|
||||||
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
|
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*/
|
/*Handle after command sent ready*/
|
||||||
static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
|
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 return 1;
|
||||||
} else if (host->cmd->data->flags & MMC_DATA_WRITE) {
|
} else if (host->cmd->data->flags & MMC_DATA_WRITE) {
|
||||||
/*After sendding multi-block-write command, start DMA transfer*/
|
/*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_TXBUFE | AT91_MCI_BLKE);
|
||||||
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
|
|
||||||
at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
|
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) {
|
||||||
|
|
||||||
if ( data->blksz & 0x3 ) {
|
if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) {
|
||||||
pr_debug("Unsupported block size\n");
|
if (data->blksz & 0x3) {
|
||||||
cmd->error = -EINVAL;
|
pr_debug("Unsupported block size\n");
|
||||||
mmc_request_done(host->mmc, host->request);
|
cmd->error = -EINVAL;
|
||||||
return;
|
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;
|
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;
|
ier = AT91_MCI_CMDRDY;
|
||||||
} else {
|
} else {
|
||||||
/* zero block length and PDC mode */
|
/* zero block length and PDC mode */
|
||||||
mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
|
mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff;
|
||||||
at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
|
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
|
* Disable the PDC controller
|
||||||
@@ -508,6 +614,13 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
|
|||||||
* Handle a write
|
* Handle a write
|
||||||
*/
|
*/
|
||||||
host->total_length = block_length * blocks;
|
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->buffer = dma_alloc_coherent(NULL,
|
||||||
host->total_length,
|
host->total_length,
|
||||||
&host->physical_address, GFP_KERNEL);
|
&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);
|
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_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;
|
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) {
|
else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
|
||||||
host->flags |= FL_SENT_STOP;
|
host->flags |= FL_SENT_STOP;
|
||||||
at91_mci_send_command(host, host->request->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);
|
mmc_request_done(host->mmc, host->request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle a command that has been completed
|
* 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;
|
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[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
|
||||||
cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
|
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;
|
host->buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = at91_mci_read(host, AT91_MCI_SR);
|
pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n",
|
||||||
|
status, at91_mci_read(host, AT91_MCI_SR),
|
||||||
pr_debug("Status = %08X [%08X %08X %08X %08X]\n",
|
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
|
||||||
status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
|
|
||||||
|
|
||||||
if (status & AT91_MCI_ERRORS) {
|
if (status & AT91_MCI_ERRORS) {
|
||||||
if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
|
if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
|
||||||
cmd->error = 0;
|
cmd->error = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
|
if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) {
|
||||||
cmd->error = -ETIMEDOUT;
|
if (data) {
|
||||||
else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
|
if (status & AT91_MCI_DTOE)
|
||||||
cmd->error = -EILSEQ;
|
data->error = -ETIMEDOUT;
|
||||||
else
|
else if (status & AT91_MCI_DCRCE)
|
||||||
cmd->error = -EIO;
|
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",
|
pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n",
|
||||||
cmd->error, cmd->opcode, cmd->retries);
|
cmd->error, data ? data->error : 0,
|
||||||
|
cmd->opcode, cmd->retries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -613,6 +743,8 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||||||
host->request = mrq;
|
host->request = mrq;
|
||||||
host->flags = 0;
|
host->flags = 0;
|
||||||
|
|
||||||
|
mod_timer(&host->timer, jiffies + HZ);
|
||||||
|
|
||||||
at91_mci_process_next(host);
|
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) {
|
if (int_status & AT91_MCI_NOTBUSY) {
|
||||||
pr_debug("Card is ready\n");
|
pr_debug("Card is ready\n");
|
||||||
|
at91_mci_update_bytes_xfered(host);
|
||||||
completed = 1;
|
completed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,9 +877,21 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
|
|||||||
|
|
||||||
if (int_status & AT91_MCI_BLKE) {
|
if (int_status & AT91_MCI_BLKE) {
|
||||||
pr_debug("Block transfer has ended\n");
|
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)
|
if (int_status & AT91_MCI_TXRDY)
|
||||||
pr_debug("Ready to transmit\n");
|
pr_debug("Ready to transmit\n");
|
||||||
|
|
||||||
@@ -761,10 +906,10 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
|
|||||||
|
|
||||||
if (completed) {
|
if (completed) {
|
||||||
pr_debug("Completed command\n");
|
pr_debug("Completed command\n");
|
||||||
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
|
at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
|
||||||
at91_mci_completed_command(host);
|
at91_mci_completed_command(host, int_status);
|
||||||
} else
|
} 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;
|
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)
|
static int at91_mci_get_ro(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
int read_only = 0;
|
|
||||||
struct at91mci_host *host = mmc_priv(mmc);
|
struct at91mci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
if (host->board->wp_pin) {
|
if (host->board->wp_pin)
|
||||||
read_only = gpio_get_value(host->board->wp_pin);
|
return !!gpio_get_value(host->board->wp_pin);
|
||||||
printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc),
|
/*
|
||||||
(read_only ? "read-only" : "read-write") );
|
* Board doesn't support read only detection; let the mmc core
|
||||||
}
|
* decide what to do.
|
||||||
else {
|
*/
|
||||||
printk(KERN_WARNING "%s: host does not support reading read-only "
|
return -ENOSYS;
|
||||||
"switch. Assuming write-enable.\n", mmc_hostname(mmc));
|
}
|
||||||
}
|
|
||||||
return read_only;
|
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 = {
|
static const struct mmc_host_ops at91_mci_ops = {
|
||||||
.request = at91_mci_request,
|
.request = at91_mci_request,
|
||||||
.set_ios = at91_mci_set_ios,
|
.set_ios = at91_mci_set_ios,
|
||||||
.get_ro = at91_mci_get_ro,
|
.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_min = 375000;
|
||||||
mmc->f_max = 25000000;
|
mmc->f_max = 25000000;
|
||||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
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_size = 4095;
|
||||||
mmc->max_blk_count = mmc->max_req_size;
|
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);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
|
setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* monitor card insertion/removal if we can
|
* 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);
|
at91_mci_disable(host);
|
||||||
|
del_timer_sync(&host->timer);
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
free_irq(host->irq, host);
|
free_irq(host->irq, host);
|
||||||
|
|
||||||
|
|||||||
@@ -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__ */
|
||||||
File diff suppressed because it is too large
Load Diff
+485
-339
File diff suppressed because it is too large
Load Diff
@@ -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
|
|
||||||
@@ -892,9 +892,12 @@ static int imxmci_get_ro(struct mmc_host *mmc)
|
|||||||
struct imxmci_host *host = mmc_priv(mmc);
|
struct imxmci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
if (host->pdata && host->pdata->get_ro)
|
if (host->pdata && host->pdata->get_ro)
|
||||||
return host->pdata->get_ro(mmc_dev(mmc));
|
return !!host->pdata->get_ro(mmc_dev(mmc));
|
||||||
/* Host doesn't support read only detection so assume writeable */
|
/*
|
||||||
return 0;
|
* Board doesn't support read only detection; let the mmc core
|
||||||
|
* decide what to do.
|
||||||
|
*/
|
||||||
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user