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

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (83 commits)
  mmc: fix compile error when CONFIG_BLOCK is not enabled
  mmc: core: Cleanup eMMC4.5 conditionals
  mmc: omap_hsmmc: if multiblock reads are broken, disable them
  mmc: core: add workaround for controllers with broken multiblock reads
  mmc: core: Prevent too long response times for suspend
  mmc: recognise SDIO cards with SDIO_CCCR_REV 3.00
  mmc: sd: Handle SD3.0 cards not supporting UHS-I bus speed mode
  mmc: core: support HPI send command
  mmc: core: Add cache control for eMMC4.5 device
  mmc: core: Modify the timeout value for writing power class
  mmc: core: new discard feature support at eMMC v4.5
  mmc: core: mmc sanitize feature support for v4.5
  mmc: dw_mmc: modify DATA register offset
  mmc: sdhci-pci: add flag for devices that can support runtime PM
  mmc: omap_hsmmc: ensure pbias configuration is always done
  mmc: core: Add Power Off Notify Feature eMMC 4.5
  mmc: sdhci-s3c: fix potential NULL dereference
  mmc: replace printk with appropriate display macro
  mmc: core: Add default timeout value for CMD6
  mmc: sdhci-pci: add runtime pm support
  ...
This commit is contained in:
Linus Torvalds 2011-10-28 14:16:11 -07:00
commit 46b51ea209
81 changed files with 3151 additions and 1067 deletions

View File

@ -0,0 +1,27 @@
* NVIDIA Tegra Secure Digital Host Controller
This controller on Tegra family SoCs provides an interface for MMC, SD,
and SDIO types of memory cards.
Required properties:
- compatible : Should be "nvidia,<chip>-sdhci"
- reg : Should contain SD/MMC registers location and length
- interrupts : Should contain SD/MMC interrupt
Optional properties:
- cd-gpios : Specify GPIOs for card detection
- wp-gpios : Specify GPIOs for write protection
- power-gpios : Specify GPIOs for power control
- support-8bit : Boolean, indicates if 8-bit mode should be used.
Example:
sdhci@c8000200 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000200 0x200>;
interrupts = <47>;
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 155 0>; /* gpio PT3 */
support-8bit;
};

View File

@ -21,6 +21,11 @@ o fail_make_request
/sys/block/<device>/make-it-fail or /sys/block/<device>/make-it-fail or
/sys/block/<device>/<partition>/make-it-fail. (generic_make_request()) /sys/block/<device>/<partition>/make-it-fail. (generic_make_request())
o fail_mmc_request
injects MMC data errors on devices permitted by setting
debugfs entries under /sys/kernel/debug/mmc0/fail_mmc_request
Configure fault-injection capabilities behavior Configure fault-injection capabilities behavior
----------------------------------------------- -----------------------------------------------
@ -115,7 +120,8 @@ use the boot option:
failslab= failslab=
fail_page_alloc= fail_page_alloc=
fail_make_request=<interval>,<probability>,<space>,<times> fail_make_request=
mmc_core.fail_request=<interval>,<probability>,<space>,<times>
How to add new fault injection capability How to add new fault injection capability
----------------------------------------- -----------------------------------------

View File

@ -319,7 +319,7 @@ void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data)
if (!data) if (!data)
return; return;
for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) { for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (data->slot[i].bus_width) { if (data->slot[i].bus_width) {
/* input/irq */ /* input/irq */
if (data->slot[i].detect_pin) { if (data->slot[i].detect_pin) {

View File

@ -175,12 +175,6 @@ static struct resource resources_sdc1[] = {
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
}, },
{
.start = INT_SDC1_1,
.end = INT_SDC1_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
},
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
.name = "status_irq" .name = "status_irq"
@ -203,12 +197,6 @@ static struct resource resources_sdc2[] = {
.end = INT_SDC2_0, .end = INT_SDC2_0,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
},
{
.start = INT_SDC2_1,
.end = INT_SDC2_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
}, },
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
@ -232,12 +220,6 @@ static struct resource resources_sdc3[] = {
.end = INT_SDC3_0, .end = INT_SDC3_0,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
},
{
.start = INT_SDC3_1,
.end = INT_SDC3_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
}, },
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
@ -261,12 +243,6 @@ static struct resource resources_sdc4[] = {
.end = INT_SDC4_0, .end = INT_SDC4_0,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
},
{
.start = INT_SDC4_1,
.end = INT_SDC4_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
}, },
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,

View File

@ -139,12 +139,6 @@ static struct resource resources_sdc1[] = {
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
}, },
{
.start = INT_SDC1_1,
.end = INT_SDC1_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
},
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
.name = "status_irq" .name = "status_irq"
@ -167,12 +161,6 @@ static struct resource resources_sdc2[] = {
.end = INT_SDC2_0, .end = INT_SDC2_0,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
},
{
.start = INT_SDC2_1,
.end = INT_SDC2_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
}, },
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
@ -196,12 +184,6 @@ static struct resource resources_sdc3[] = {
.end = INT_SDC3_0, .end = INT_SDC3_0,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
},
{
.start = INT_SDC3_1,
.end = INT_SDC3_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
}, },
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,
@ -225,12 +207,6 @@ static struct resource resources_sdc4[] = {
.end = INT_SDC4_0, .end = INT_SDC4_0,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
.name = "cmd_irq", .name = "cmd_irq",
},
{
.start = INT_SDC4_1,
.end = INT_SDC4_1,
.flags = IORESOURCE_IRQ,
.name = "pio_irq",
}, },
{ {
.flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED,

View File

@ -8,13 +8,6 @@
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
struct embedded_sdio_data {
struct sdio_cis cis;
struct sdio_cccr cccr;
struct sdio_embedded_func *funcs;
int num_funcs;
};
struct msm_mmc_gpio { struct msm_mmc_gpio {
unsigned no; unsigned no;
const char *name; const char *name;
@ -29,9 +22,9 @@ struct msm_mmc_platform_data {
unsigned int ocr_mask; /* available voltages */ unsigned int ocr_mask; /* available voltages */
u32 (*translate_vdd)(struct device *, unsigned int); u32 (*translate_vdd)(struct device *, unsigned int);
unsigned int (*status)(struct device *); unsigned int (*status)(struct device *);
struct embedded_sdio_data *embedded_sdio;
int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
struct msm_mmc_gpio_data *gpio_data; struct msm_mmc_gpio_data *gpio_data;
void (*init_card)(struct mmc_card *card);
}; };
#endif #endif

View File

@ -355,14 +355,17 @@ static struct resource sdhi0_resources[] = {
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { [1] = {
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
.start = gic_spi(83), .start = gic_spi(83),
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[2] = { [2] = {
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = gic_spi(84), .start = gic_spi(84),
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[3] = { [3] = {
.name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = gic_spi(85), .start = gic_spi(85),
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
@ -398,14 +401,17 @@ static struct resource sdhi1_resources[] = {
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { [1] = {
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
.start = gic_spi(87), .start = gic_spi(87),
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[2] = { [2] = {
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = gic_spi(88), .start = gic_spi(88),
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[3] = { [3] = {
.name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = gic_spi(89), .start = gic_spi(89),
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },

View File

@ -1072,14 +1072,17 @@ static struct resource sdhi1_resources[] = {
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { [1] = {
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
.start = evt2irq(0x0e80), /* SDHI1_SDHI1I0 */ .start = evt2irq(0x0e80), /* SDHI1_SDHI1I0 */
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[2] = { [2] = {
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = evt2irq(0x0ea0), /* SDHI1_SDHI1I1 */ .start = evt2irq(0x0ea0), /* SDHI1_SDHI1I1 */
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[3] = { [3] = {
.name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = evt2irq(0x0ec0), /* SDHI1_SDHI1I2 */ .start = evt2irq(0x0ec0), /* SDHI1_SDHI1I2 */
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
@ -1123,14 +1126,17 @@ static struct resource sdhi2_resources[] = {
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { [1] = {
.name = SH_MOBILE_SDHI_IRQ_CARD_DETECT,
.start = evt2irq(0x1200), /* SDHI2_SDHI2I0 */ .start = evt2irq(0x1200), /* SDHI2_SDHI2I0 */
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[2] = { [2] = {
.name = SH_MOBILE_SDHI_IRQ_SDCARD,
.start = evt2irq(0x1220), /* SDHI2_SDHI2I1 */ .start = evt2irq(0x1220), /* SDHI2_SDHI2I1 */
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
[3] = { [3] = {
.name = SH_MOBILE_SDHI_IRQ_SDIO,
.start = evt2irq(0x1240), /* SDHI2_SDHI2I2 */ .start = evt2irq(0x1240), /* SDHI2_SDHI2I2 */
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },

View File

@ -31,7 +31,24 @@
#define OMAP_MMC_MAX_SLOTS 2 #define OMAP_MMC_MAX_SLOTS 2
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(1) /*
* struct omap_mmc_dev_attr.flags possibilities
*
* OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can
* operate with either 1.8Vdc or 3.0Vdc card voltages; this flag
* should be set if this is the case. See for example Section 22.5.3
* "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia
* Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R).
*
* OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers
* don't work correctly on some MMC controller instances on some
* OMAP3 SoCs; this flag should be set if this is the case. See
* for example Advisory 2.1.1.128 "MMC: Multiple Block Read
* Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
* Revision F (October 2010) (SPRZ278F).
*/
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
struct omap_mmc_dev_attr { struct omap_mmc_dev_attr {
u8 flags; u8 flags;

View File

@ -94,6 +94,11 @@ struct mmc_blk_data {
unsigned int read_only; unsigned int read_only;
unsigned int part_type; unsigned int part_type;
unsigned int name_idx; unsigned int name_idx;
unsigned int reset_done;
#define MMC_BLK_READ BIT(0)
#define MMC_BLK_WRITE BIT(1)
#define MMC_BLK_DISCARD BIT(2)
#define MMC_BLK_SECDISCARD BIT(3)
/* /*
* Only set in main mmc_blk_data associated * Only set in main mmc_blk_data associated
@ -109,11 +114,11 @@ static DEFINE_MUTEX(open_lock);
enum mmc_blk_status { enum mmc_blk_status {
MMC_BLK_SUCCESS = 0, MMC_BLK_SUCCESS = 0,
MMC_BLK_PARTIAL, MMC_BLK_PARTIAL,
MMC_BLK_RETRY,
MMC_BLK_RETRY_SINGLE,
MMC_BLK_DATA_ERR,
MMC_BLK_CMD_ERR, MMC_BLK_CMD_ERR,
MMC_BLK_RETRY,
MMC_BLK_ABORT, MMC_BLK_ABORT,
MMC_BLK_DATA_ERR,
MMC_BLK_ECC_ERR,
}; };
module_param(perdev_minors, int, 0444); module_param(perdev_minors, int, 0444);
@ -291,7 +296,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_card *card; struct mmc_card *card;
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct scatterlist sg; struct scatterlist sg;
int err; int err;
@ -442,19 +447,24 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
{ {
int ret; int ret;
struct mmc_blk_data *main_md = mmc_get_drvdata(card); struct mmc_blk_data *main_md = mmc_get_drvdata(card);
if (main_md->part_curr == md->part_type) if (main_md->part_curr == md->part_type)
return 0; return 0;
if (mmc_card_mmc(card)) { if (mmc_card_mmc(card)) {
card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; u8 part_config = card->ext_csd.part_config;
card->ext_csd.part_config |= md->part_type;
part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
part_config |= md->part_type;
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_PART_CONFIG, card->ext_csd.part_config, EXT_CSD_PART_CONFIG, part_config,
card->ext_csd.part_time); card->ext_csd.part_time);
if (ret) if (ret)
return ret; return ret;
}
card->ext_csd.part_config = part_config;
}
main_md->part_curr = md->part_type; main_md->part_curr = md->part_type;
return 0; return 0;
@ -466,7 +476,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
u32 result; u32 result;
__be32 *blocks; __be32 *blocks;
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
unsigned int timeout_us; unsigned int timeout_us;
@ -616,7 +626,7 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
* Otherwise we don't understand what happened, so abort. * Otherwise we don't understand what happened, so abort.
*/ */
static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
struct mmc_blk_request *brq) struct mmc_blk_request *brq, int *ecc_err)
{ {
bool prev_cmd_status_valid = true; bool prev_cmd_status_valid = true;
u32 status, stop_status = 0; u32 status, stop_status = 0;
@ -641,6 +651,12 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
if (err) if (err)
return ERR_ABORT; return ERR_ABORT;
/* Flag ECC errors */
if ((status & R1_CARD_ECC_FAILED) ||
(brq->stop.resp[0] & R1_CARD_ECC_FAILED) ||
(brq->cmd.resp[0] & R1_CARD_ECC_FAILED))
*ecc_err = 1;
/* /*
* Check the current card state. If it is in some data transfer * Check the current card state. If it is in some data transfer
* mode, tell it to stop (and hopefully transition back to TRAN.) * mode, tell it to stop (and hopefully transition back to TRAN.)
@ -658,6 +674,8 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
*/ */
if (err) if (err)
return ERR_ABORT; return ERR_ABORT;
if (stop_status & R1_CARD_ECC_FAILED)
*ecc_err = 1;
} }
/* Check for set block count errors */ /* Check for set block count errors */
@ -670,6 +688,10 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error, return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
prev_cmd_status_valid, status); prev_cmd_status_valid, status);
/* Data errors */
if (!brq->stop.error)
return ERR_CONTINUE;
/* Now for stop errors. These aren't fatal to the transfer. */ /* Now for stop errors. These aren't fatal to the transfer. */
pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n", pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
req->rq_disk->disk_name, brq->stop.error, req->rq_disk->disk_name, brq->stop.error,
@ -686,12 +708,45 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
return ERR_CONTINUE; return ERR_CONTINUE;
} }
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
int type)
{
int err;
if (md->reset_done & type)
return -EEXIST;
md->reset_done |= type;
err = mmc_hw_reset(host);
/* Ensure we switch back to the correct partition */
if (err != -EOPNOTSUPP) {
struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
int part_err;
main_md->part_curr = main_md->part_type;
part_err = mmc_blk_part_switch(host->card, md);
if (part_err) {
/*
* We have failed to get back into the correct
* partition, so we need to abort the whole request.
*/
return -ENODEV;
}
}
return err;
}
static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
{
md->reset_done &= ~type;
}
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
{ {
struct mmc_blk_data *md = mq->data; struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card; struct mmc_card *card = md->queue.card;
unsigned int from, nr, arg; unsigned int from, nr, arg;
int err = 0; int err = 0, type = MMC_BLK_DISCARD;
if (!mmc_can_erase(card)) { if (!mmc_can_erase(card)) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
@ -701,11 +756,13 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
from = blk_rq_pos(req); from = blk_rq_pos(req);
nr = blk_rq_sectors(req); nr = blk_rq_sectors(req);
if (mmc_can_trim(card)) if (mmc_can_discard(card))
arg = MMC_DISCARD_ARG;
else if (mmc_can_trim(card))
arg = MMC_TRIM_ARG; arg = MMC_TRIM_ARG;
else else
arg = MMC_ERASE_ARG; arg = MMC_ERASE_ARG;
retry:
if (card->quirks & MMC_QUIRK_INAND_CMD38) { if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
INAND_CMD38_ARG_EXT_CSD, INAND_CMD38_ARG_EXT_CSD,
@ -718,6 +775,10 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
} }
err = mmc_erase(card, from, nr, arg); err = mmc_erase(card, from, nr, arg);
out: out:
if (err == -EIO && !mmc_blk_reset(md, card->host, type))
goto retry;
if (!err)
mmc_blk_reset_success(md, type);
spin_lock_irq(&md->lock); spin_lock_irq(&md->lock);
__blk_end_request(req, err, blk_rq_bytes(req)); __blk_end_request(req, err, blk_rq_bytes(req));
spin_unlock_irq(&md->lock); spin_unlock_irq(&md->lock);
@ -731,13 +792,20 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
struct mmc_blk_data *md = mq->data; struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card; struct mmc_card *card = md->queue.card;
unsigned int from, nr, arg; unsigned int from, nr, arg;
int err = 0; int err = 0, type = MMC_BLK_SECDISCARD;
if (!mmc_can_secure_erase_trim(card)) { if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out; goto out;
} }
/* The sanitize operation is supported at v4.5 only */
if (mmc_can_sanitize(card)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_SANITIZE_START, 1, 0);
goto out;
}
from = blk_rq_pos(req); from = blk_rq_pos(req);
nr = blk_rq_sectors(req); nr = blk_rq_sectors(req);
@ -745,7 +813,7 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
arg = MMC_SECURE_TRIM1_ARG; arg = MMC_SECURE_TRIM1_ARG;
else else
arg = MMC_SECURE_ERASE_ARG; arg = MMC_SECURE_ERASE_ARG;
retry:
if (card->quirks & MMC_QUIRK_INAND_CMD38) { if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
INAND_CMD38_ARG_EXT_CSD, INAND_CMD38_ARG_EXT_CSD,
@ -769,6 +837,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG); err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
} }
out: out:
if (err == -EIO && !mmc_blk_reset(md, card->host, type))
goto retry;
if (!err)
mmc_blk_reset_success(md, type);
spin_lock_irq(&md->lock); spin_lock_irq(&md->lock);
__blk_end_request(req, err, blk_rq_bytes(req)); __blk_end_request(req, err, blk_rq_bytes(req));
spin_unlock_irq(&md->lock); spin_unlock_irq(&md->lock);
@ -779,16 +851,18 @@ out:
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req) static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
{ {
struct mmc_blk_data *md = mq->data; struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
int ret = 0;
ret = mmc_flush_cache(card);
if (ret)
ret = -EIO;
/*
* No-op, only service this because we need REQ_FUA for reliable
* writes.
*/
spin_lock_irq(&md->lock); spin_lock_irq(&md->lock);
__blk_end_request_all(req, 0); __blk_end_request_all(req, ret);
spin_unlock_irq(&md->lock); spin_unlock_irq(&md->lock);
return 1; return ret ? 0 : 1;
} }
/* /*
@ -825,11 +899,11 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
static int mmc_blk_err_check(struct mmc_card *card, static int mmc_blk_err_check(struct mmc_card *card,
struct mmc_async_req *areq) struct mmc_async_req *areq)
{ {
enum mmc_blk_status ret = MMC_BLK_SUCCESS;
struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req, struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
mmc_active); mmc_active);
struct mmc_blk_request *brq = &mq_mrq->brq; struct mmc_blk_request *brq = &mq_mrq->brq;
struct request *req = mq_mrq->req; struct request *req = mq_mrq->req;
int ecc_err = 0;
/* /*
* sbc.error indicates a problem with the set block count * sbc.error indicates a problem with the set block count
@ -841,8 +915,9 @@ static int mmc_blk_err_check(struct mmc_card *card,
* stop.error indicates a problem with the stop command. Data * stop.error indicates a problem with the stop command. Data
* may have been transferred, or may still be transferring. * may have been transferred, or may still be transferring.
*/ */
if (brq->sbc.error || brq->cmd.error || brq->stop.error) { if (brq->sbc.error || brq->cmd.error || brq->stop.error ||
switch (mmc_blk_cmd_recovery(card, req, brq)) { brq->data.error) {
switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err)) {
case ERR_RETRY: case ERR_RETRY:
return MMC_BLK_RETRY; return MMC_BLK_RETRY;
case ERR_ABORT: case ERR_ABORT:
@ -873,7 +948,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
do { do {
int err = get_card_status(card, &status, 5); int err = get_card_status(card, &status, 5);
if (err) { if (err) {
printk(KERN_ERR "%s: error %d requesting status\n", pr_err("%s: error %d requesting status\n",
req->rq_disk->disk_name, err); req->rq_disk->disk_name, err);
return MMC_BLK_CMD_ERR; return MMC_BLK_CMD_ERR;
} }
@ -894,23 +969,21 @@ static int mmc_blk_err_check(struct mmc_card *card,
brq->cmd.resp[0], brq->stop.resp[0]); brq->cmd.resp[0], brq->stop.resp[0]);
if (rq_data_dir(req) == READ) { if (rq_data_dir(req) == READ) {
if (brq->data.blocks > 1) { if (ecc_err)
/* Redo read one sector at a time */ return MMC_BLK_ECC_ERR;
pr_warning("%s: retrying using single block read\n",
req->rq_disk->disk_name);
return MMC_BLK_RETRY_SINGLE;
}
return MMC_BLK_DATA_ERR; return MMC_BLK_DATA_ERR;
} else { } else {
return MMC_BLK_CMD_ERR; return MMC_BLK_CMD_ERR;
} }
} }
if (ret == MMC_BLK_SUCCESS && if (!brq->data.bytes_xfered)
blk_rq_bytes(req) != brq->data.bytes_xfered) return MMC_BLK_RETRY;
ret = MMC_BLK_PARTIAL;
return ret; if (blk_rq_bytes(req) != brq->data.bytes_xfered)
return MMC_BLK_PARTIAL;
return MMC_BLK_SUCCESS;
} }
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
@ -957,13 +1030,20 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
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 (brq->data.blocks > 1) {
* After a read error, we redo the request one sector at a time /*
* in order to accurately determine which sectors can be read * After a read error, we redo the request one sector
* successfully. * at a time in order to accurately determine which
*/ * sectors can be read successfully.
if (disable_multi && brq->data.blocks > 1) */
brq->data.blocks = 1; if (disable_multi)
brq->data.blocks = 1;
/* Some controllers can't do multiblock reads due to hw bugs */
if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ &&
rq_data_dir(req) == READ)
brq->data.blocks = 1;
}
if (brq->data.blocks > 1 || do_rel_wr) { if (brq->data.blocks > 1 || do_rel_wr) {
/* SPI multiblock writes terminate using a special /* SPI multiblock writes terminate using a special
@ -1049,12 +1129,41 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
mmc_queue_bounce_pre(mqrq); mmc_queue_bounce_pre(mqrq);
} }
static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
struct mmc_blk_request *brq, struct request *req,
int ret)
{
/*
* If this is an SD card and we're writing, we can first
* mark the known good sectors as ok.
*
* If the card is not SD, we can still ok written sectors
* as reported by the controller (which might be less than
* the real number of written sectors, but never more).
*/
if (mmc_card_sd(card)) {
u32 blocks;
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
return ret;
}
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
{ {
struct mmc_blk_data *md = mq->data; struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card; struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mq->mqrq_cur->brq; struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
int ret = 1, disable_multi = 0, retry = 0; int ret = 1, disable_multi = 0, retry = 0, type;
enum mmc_blk_status status; enum mmc_blk_status status;
struct mmc_queue_req *mq_rq; struct mmc_queue_req *mq_rq;
struct request *req; struct request *req;
@ -1076,6 +1185,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
brq = &mq_rq->brq; brq = &mq_rq->brq;
req = mq_rq->req; req = mq_rq->req;
type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
mmc_queue_bounce_post(mq_rq); mmc_queue_bounce_post(mq_rq);
switch (status) { switch (status) {
@ -1084,18 +1194,18 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
/* /*
* A block was successfully transferred. * A block was successfully transferred.
*/ */
mmc_blk_reset_success(md, type);
spin_lock_irq(&md->lock); spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, ret = __blk_end_request(req, 0,
brq->data.bytes_xfered); brq->data.bytes_xfered);
spin_unlock_irq(&md->lock); spin_unlock_irq(&md->lock);
/*
* If the blk_end_request function returns non-zero even
* though all data has been transferred and no errors
* were returned by the host controller, it's a bug.
*/
if (status == MMC_BLK_SUCCESS && ret) { if (status == MMC_BLK_SUCCESS && ret) {
/* pr_err("%s BUG rq_tot %d d_xfer %d\n",
* The blk_end_request has returned non zero
* even though all data is transfered and no
* erros returned by host.
* If this happen it's a bug.
*/
printk(KERN_ERR "%s BUG rq_tot %d d_xfer %d\n",
__func__, blk_rq_bytes(req), __func__, blk_rq_bytes(req),
brq->data.bytes_xfered); brq->data.bytes_xfered);
rqc = NULL; rqc = NULL;
@ -1103,16 +1213,36 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
} }
break; break;
case MMC_BLK_CMD_ERR: case MMC_BLK_CMD_ERR:
goto cmd_err; ret = mmc_blk_cmd_err(md, card, brq, req, ret);
case MMC_BLK_RETRY_SINGLE: if (!mmc_blk_reset(md, card->host, type))
disable_multi = 1; break;
break; goto cmd_abort;
case MMC_BLK_RETRY: case MMC_BLK_RETRY:
if (retry++ < 5) if (retry++ < 5)
break; break;
/* Fall through */
case MMC_BLK_ABORT: case MMC_BLK_ABORT:
if (!mmc_blk_reset(md, card->host, type))
break;
goto cmd_abort; goto cmd_abort;
case MMC_BLK_DATA_ERR: case MMC_BLK_DATA_ERR: {
int err;
err = mmc_blk_reset(md, card->host, type);
if (!err)
break;
if (err == -ENODEV)
goto cmd_abort;
/* Fall through */
}
case MMC_BLK_ECC_ERR:
if (brq->data.blocks > 1) {
/* Redo read one sector at a time */
pr_warning("%s: retrying using single block read\n",
req->rq_disk->disk_name);
disable_multi = 1;
break;
}
/* /*
* After an error, we redo I/O one sector at a * After an error, we redo I/O one sector at a
* time, so we only reach here after trying to * time, so we only reach here after trying to
@ -1129,7 +1259,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
if (ret) { if (ret) {
/* /*
* In case of a none complete request * In case of a incomplete request
* prepare it again and resend. * prepare it again and resend.
*/ */
mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq); mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
@ -1139,30 +1269,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
return 1; return 1;
cmd_err:
/*
* If this is an SD card and we're writing, we can first
* mark the known good sectors as ok.
*
* If the card is not SD, we can still ok written sectors
* as reported by the controller (which might be less than
* the real number of written sectors, but never more).
*/
if (mmc_card_sd(card)) {
u32 blocks;
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
cmd_abort: cmd_abort:
spin_lock_irq(&md->lock); spin_lock_irq(&md->lock);
while (ret) while (ret)
@ -1190,6 +1296,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
ret = mmc_blk_part_switch(card, md); ret = mmc_blk_part_switch(card, md);
if (ret) { if (ret) {
if (req) {
spin_lock_irq(&md->lock);
__blk_end_request_all(req, -EIO);
spin_unlock_irq(&md->lock);
}
ret = 0; ret = 0;
goto out; goto out;
} }
@ -1374,32 +1485,35 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2, string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2,
cap_str, sizeof(cap_str)); cap_str, sizeof(cap_str));
printk(KERN_INFO "%s: %s %s partition %u %s\n", pr_info("%s: %s %s partition %u %s\n",
part_md->disk->disk_name, mmc_card_id(card), part_md->disk->disk_name, mmc_card_id(card),
mmc_card_name(card), part_md->part_type, cap_str); mmc_card_name(card), part_md->part_type, cap_str);
return 0; return 0;
} }
/* MMC Physical partitions consist of two boot partitions and
* up to four general purpose partitions.
* For each partition enabled in EXT_CSD a block device will be allocatedi
* to provide access to the partition.
*/
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
{ {
int ret = 0; int idx, ret = 0;
if (!mmc_card_mmc(card)) if (!mmc_card_mmc(card))
return 0; return 0;
if (card->ext_csd.boot_size) { for (idx = 0; idx < card->nr_parts; idx++) {
ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0, if (card->part[idx].size) {
card->ext_csd.boot_size >> 9, ret = mmc_blk_alloc_part(card, md,
true, card->part[idx].part_cfg,
"boot0"); card->part[idx].size >> 9,
if (ret) card->part[idx].force_ro,
return ret; card->part[idx].name);
ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT1, if (ret)
card->ext_csd.boot_size >> 9, return ret;
true, }
"boot1");
if (ret)
return ret;
} }
return ret; return ret;
@ -1415,7 +1529,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
mmc_release_host(card->host); mmc_release_host(card->host);
if (err) { if (err) {
printk(KERN_ERR "%s: unable to set block size to 512: %d\n", pr_err("%s: unable to set block size to 512: %d\n",
md->disk->disk_name, err); md->disk->disk_name, err);
return -EINVAL; return -EINVAL;
} }
@ -1517,7 +1631,7 @@ static int mmc_blk_probe(struct mmc_card *card)
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2, string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
cap_str, sizeof(cap_str)); cap_str, sizeof(cap_str));
printk(KERN_INFO "%s: %s %s %s %s\n", pr_info("%s: %s %s %s %s\n",
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
cap_str, md->read_only ? "(ro)" : ""); cap_str, md->read_only ? "(ro)" : "");

View File

@ -22,6 +22,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/module.h>
#define RESULT_OK 0 #define RESULT_OK 0
#define RESULT_FAIL 1 #define RESULT_FAIL 1
@ -250,7 +251,7 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
if (!busy && mmc_test_busy(&cmd)) { if (!busy && mmc_test_busy(&cmd)) {
busy = 1; busy = 1;
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
printk(KERN_INFO "%s: Warning: Host did not " pr_info("%s: Warning: Host did not "
"wait for busy state to end.\n", "wait for busy state to end.\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
} }
@ -552,7 +553,7 @@ static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
rate = mmc_test_rate(bytes, &ts); rate = mmc_test_rate(bytes, &ts);
iops = mmc_test_rate(100, &ts); /* I/O ops per sec x 100 */ iops = mmc_test_rate(100, &ts); /* I/O ops per sec x 100 */
printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu " pr_info("%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
"seconds (%u kB/s, %u KiB/s, %u.%02u IOPS)\n", "seconds (%u kB/s, %u KiB/s, %u.%02u IOPS)\n",
mmc_hostname(test->card->host), sectors, sectors >> 1, mmc_hostname(test->card->host), sectors, sectors >> 1,
(sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec, (sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
@ -578,7 +579,7 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
rate = mmc_test_rate(tot, &ts); rate = mmc_test_rate(tot, &ts);
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */ iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took " pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
"%lu.%09lu seconds (%u kB/s, %u KiB/s, " "%lu.%09lu seconds (%u kB/s, %u KiB/s, "
"%u.%02u IOPS, sg_len %d)\n", "%u.%02u IOPS, sg_len %d)\n",
mmc_hostname(test->card->host), count, sectors, count, mmc_hostname(test->card->host), count, sectors, count,
@ -1408,7 +1409,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
static int mmc_test_no_highmem(struct mmc_test_card *test) static int mmc_test_no_highmem(struct mmc_test_card *test)
{ {
printk(KERN_INFO "%s: Highmem not configured - test skipped\n", pr_info("%s: Highmem not configured - test skipped\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
return 0; return 0;
} }
@ -1435,7 +1436,7 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
t->max_seg_sz, &t->sg_len, min_sg_len); t->max_seg_sz, &t->sg_len, min_sg_len);
} }
if (err) if (err)
printk(KERN_INFO "%s: Failed to map sg list\n", pr_info("%s: Failed to map sg list\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
return err; return err;
} }
@ -2135,7 +2136,7 @@ static int mmc_test_rw_multiple(struct mmc_test_card *test,
return ret; return ret;
err: err:
printk(KERN_INFO "[%s] error\n", __func__); pr_info("[%s] error\n", __func__);
return ret; return ret;
} }
@ -2149,7 +2150,7 @@ static int mmc_test_rw_multiple_size(struct mmc_test_card *test,
if (rw->do_nonblock_req && if (rw->do_nonblock_req &&
((!pre_req && post_req) || (pre_req && !post_req))) { ((!pre_req && post_req) || (pre_req && !post_req))) {
printk(KERN_INFO "error: only one of pre/post is defined\n"); pr_info("error: only one of pre/post is defined\n");
return -EINVAL; return -EINVAL;
} }
@ -2328,6 +2329,31 @@ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test)
return mmc_test_rw_multiple_sg_len(test, &test_data); return mmc_test_rw_multiple_sg_len(test, &test_data);
} }
/*
* eMMC hardware reset.
*/
static int mmc_test_hw_reset(struct mmc_test_card *test)
{
struct mmc_card *card = test->card;
struct mmc_host *host = card->host;
int err;
err = mmc_hw_reset_check(host);
if (!err)
return RESULT_OK;
if (err == -ENOSYS)
return RESULT_FAIL;
if (err != -EOPNOTSUPP)
return err;
if (!mmc_can_reset(card))
return RESULT_UNSUP_CARD;
return RESULT_UNSUP_HOST;
}
static const struct mmc_test_case mmc_test_cases[] = { static const struct mmc_test_case mmc_test_cases[] = {
{ {
.name = "Basic write (no data verification)", .name = "Basic write (no data verification)",
@ -2650,6 +2676,11 @@ static const struct mmc_test_case mmc_test_cases[] = {
.run = mmc_test_profile_sglen_r_nonblock_perf, .run = mmc_test_profile_sglen_r_nonblock_perf,
.cleanup = mmc_test_area_cleanup, .cleanup = mmc_test_area_cleanup,
}, },
{
.name = "eMMC hardware reset",
.run = mmc_test_hw_reset,
},
}; };
static DEFINE_MUTEX(mmc_test_lock); static DEFINE_MUTEX(mmc_test_lock);
@ -2660,7 +2691,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
{ {
int i, ret; int i, ret;
printk(KERN_INFO "%s: Starting tests of card %s...\n", pr_info("%s: Starting tests of card %s...\n",
mmc_hostname(test->card->host), mmc_card_id(test->card)); mmc_hostname(test->card->host), mmc_card_id(test->card));
mmc_claim_host(test->card->host); mmc_claim_host(test->card->host);
@ -2671,14 +2702,14 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
if (testcase && ((i + 1) != testcase)) if (testcase && ((i + 1) != testcase))
continue; continue;
printk(KERN_INFO "%s: Test case %d. %s...\n", pr_info("%s: Test case %d. %s...\n",
mmc_hostname(test->card->host), i + 1, mmc_hostname(test->card->host), i + 1,
mmc_test_cases[i].name); mmc_test_cases[i].name);
if (mmc_test_cases[i].prepare) { if (mmc_test_cases[i].prepare) {
ret = mmc_test_cases[i].prepare(test); ret = mmc_test_cases[i].prepare(test);
if (ret) { if (ret) {
printk(KERN_INFO "%s: Result: Prepare " pr_info("%s: Result: Prepare "
"stage failed! (%d)\n", "stage failed! (%d)\n",
mmc_hostname(test->card->host), mmc_hostname(test->card->host),
ret); ret);
@ -2708,25 +2739,25 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
ret = mmc_test_cases[i].run(test); ret = mmc_test_cases[i].run(test);
switch (ret) { switch (ret) {
case RESULT_OK: case RESULT_OK:
printk(KERN_INFO "%s: Result: OK\n", pr_info("%s: Result: OK\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
break; break;
case RESULT_FAIL: case RESULT_FAIL:
printk(KERN_INFO "%s: Result: FAILED\n", pr_info("%s: Result: FAILED\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
break; break;
case RESULT_UNSUP_HOST: case RESULT_UNSUP_HOST:
printk(KERN_INFO "%s: Result: UNSUPPORTED " pr_info("%s: Result: UNSUPPORTED "
"(by host)\n", "(by host)\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
break; break;
case RESULT_UNSUP_CARD: case RESULT_UNSUP_CARD:
printk(KERN_INFO "%s: Result: UNSUPPORTED " pr_info("%s: Result: UNSUPPORTED "
"(by card)\n", "(by card)\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
break; break;
default: default:
printk(KERN_INFO "%s: Result: ERROR (%d)\n", pr_info("%s: Result: ERROR (%d)\n",
mmc_hostname(test->card->host), ret); mmc_hostname(test->card->host), ret);
} }
@ -2737,7 +2768,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
if (mmc_test_cases[i].cleanup) { if (mmc_test_cases[i].cleanup) {
ret = mmc_test_cases[i].cleanup(test); ret = mmc_test_cases[i].cleanup(test);
if (ret) { if (ret) {
printk(KERN_INFO "%s: Warning: Cleanup " pr_info("%s: Warning: Cleanup "
"stage failed! (%d)\n", "stage failed! (%d)\n",
mmc_hostname(test->card->host), mmc_hostname(test->card->host),
ret); ret);
@ -2747,7 +2778,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
mmc_release_host(test->card->host); mmc_release_host(test->card->host);
printk(KERN_INFO "%s: Tests completed.\n", pr_info("%s: Tests completed.\n",
mmc_hostname(test->card->host)); mmc_hostname(test->card->host));
} }

View File

@ -108,7 +108,7 @@ static void mmc_request(struct request_queue *q)
wake_up_process(mq->thread); wake_up_process(mq->thread);
} }
struct scatterlist *mmc_alloc_sg(int sg_len, int *err) static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
{ {
struct scatterlist *sg; struct scatterlist *sg;
@ -140,7 +140,7 @@ static void mmc_queue_setup_discard(struct request_queue *q,
/* granularity must not be greater than max. discard */ /* granularity must not be greater than max. discard */
if (card->pref_erase > max_discard) if (card->pref_erase > max_discard)
q->limits.discard_granularity = 0; q->limits.discard_granularity = 0;
if (mmc_can_secure_erase_trim(card)) if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))
queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q); queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
} }
@ -197,13 +197,13 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
if (bouncesz > 512) { if (bouncesz > 512) {
mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
if (!mqrq_cur->bounce_buf) { if (!mqrq_cur->bounce_buf) {
printk(KERN_WARNING "%s: unable to " pr_warning("%s: unable to "
"allocate bounce cur buffer\n", "allocate bounce cur buffer\n",
mmc_card_name(card)); mmc_card_name(card));
} }
mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
if (!mqrq_prev->bounce_buf) { if (!mqrq_prev->bounce_buf) {
printk(KERN_WARNING "%s: unable to " pr_warning("%s: unable to "
"allocate bounce prev buffer\n", "allocate bounce prev buffer\n",
mmc_card_name(card)); mmc_card_name(card));
kfree(mqrq_cur->bounce_buf); kfree(mqrq_cur->bounce_buf);

View File

@ -1082,7 +1082,7 @@ static int sdio_uart_probe(struct sdio_func *func,
return -ENOMEM; return -ENOMEM;
if (func->class == SDIO_CLASS_UART) { if (func->class == SDIO_CLASS_UART) {
printk(KERN_WARNING "%s: need info on UART class basic setup\n", pr_warning("%s: need info on UART class basic setup\n",
sdio_func_id(func)); sdio_func_id(func));
kfree(port); kfree(port);
return -ENOSYS; return -ENOSYS;
@ -1101,23 +1101,23 @@ static int sdio_uart_probe(struct sdio_func *func,
break; break;
} }
if (!tpl) { if (!tpl) {
printk(KERN_WARNING pr_warning(
"%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
sdio_func_id(func)); sdio_func_id(func));
kfree(port); kfree(port);
return -EINVAL; return -EINVAL;
} }
printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n", pr_debug("%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
sdio_func_id(func), tpl->data[2], tpl->data[3]); sdio_func_id(func), tpl->data[2], tpl->data[3]);
port->regs_offset = (tpl->data[4] << 0) | port->regs_offset = (tpl->data[4] << 0) |
(tpl->data[5] << 8) | (tpl->data[5] << 8) |
(tpl->data[6] << 16); (tpl->data[6] << 16);
printk(KERN_DEBUG "%s: regs offset = 0x%x\n", pr_debug("%s: regs offset = 0x%x\n",
sdio_func_id(func), port->regs_offset); sdio_func_id(func), port->regs_offset);
port->uartclk = tpl->data[7] * 115200; port->uartclk = tpl->data[7] * 115200;
if (port->uartclk == 0) if (port->uartclk == 0)
port->uartclk = 115200; port->uartclk = 115200;
printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n", pr_debug("%s: clk %d baudcode %u 4800-div %u\n",
sdio_func_id(func), port->uartclk, sdio_func_id(func), port->uartclk,
tpl->data[7], tpl->data[8] | (tpl->data[9] << 8)); tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
} else { } else {

View File

@ -295,7 +295,7 @@ int mmc_add_card(struct mmc_card *card)
} }
if (mmc_host_is_spi(card->host)) { if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: new %s%s%s card on SPI\n", pr_info("%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host), mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "", mmc_card_highspeed(card) ? "high speed " : "",
mmc_card_ddr_mode(card) ? "DDR " : "", mmc_card_ddr_mode(card) ? "DDR " : "",
@ -334,10 +334,10 @@ void mmc_remove_card(struct mmc_card *card)
if (mmc_card_present(card)) { if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) { if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: SPI card removed\n", pr_info("%s: SPI card removed\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
} else { } else {
printk(KERN_INFO "%s: card %04x removed\n", pr_info("%s: card %04x removed\n",
mmc_hostname(card->host), card->rca); mmc_hostname(card->host), card->rca);
} }
device_del(&card->dev); device_del(&card->dev);

View File

@ -24,6 +24,8 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/fault-inject.h>
#include <linux/random.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
@ -83,6 +85,43 @@ static void mmc_flush_scheduled_work(void)
flush_workqueue(workqueue); flush_workqueue(workqueue);
} }
#ifdef CONFIG_FAIL_MMC_REQUEST
/*
* Internal function. Inject random data errors.
* If mmc_data is NULL no errors are injected.
*/
static void mmc_should_fail_request(struct mmc_host *host,
struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = mrq->data;
static const int data_errors[] = {
-ETIMEDOUT,
-EILSEQ,
-EIO,
};
if (!data)
return;
if (cmd->error || data->error ||
!should_fail(&host->fail_mmc_request, data->blksz * data->blocks))
return;
data->error = data_errors[random32() % ARRAY_SIZE(data_errors)];
data->bytes_xfered = (random32() % (data->bytes_xfered >> 9)) << 9;
}
#else /* CONFIG_FAIL_MMC_REQUEST */
static inline void mmc_should_fail_request(struct mmc_host *host,
struct mmc_request *mrq)
{
}
#endif /* CONFIG_FAIL_MMC_REQUEST */
/** /**
* mmc_request_done - finish processing an MMC request * mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request * @host: MMC host which completed request
@ -102,13 +141,15 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
} }
if (err && cmd->retries) { if (err && cmd->retries) {
pr_debug("%s: req failed (CMD%u): %d, retrying...\n", /*
mmc_hostname(host), cmd->opcode, err); * Request starter must handle retries - see
* mmc_wait_for_req_done().
cmd->retries--; */
cmd->error = 0; if (mrq->done)
host->ops->request(host, mrq); mrq->done(mrq);
} else { } else {
mmc_should_fail_request(host, mrq);
led_trigger_event(host->led, LED_OFF); led_trigger_event(host->led, LED_OFF);
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
@ -212,7 +253,21 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
static void mmc_wait_for_req_done(struct mmc_host *host, static void mmc_wait_for_req_done(struct mmc_host *host,
struct mmc_request *mrq) struct mmc_request *mrq)
{ {
wait_for_completion(&mrq->completion); struct mmc_command *cmd;
while (1) {
wait_for_completion(&mrq->completion);
cmd = mrq->cmd;
if (!cmd->error || !cmd->retries)
break;
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
host->ops->request(host, mrq);
}
} }
/** /**
@ -279,8 +334,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
mmc_wait_for_req_done(host, host->areq->mrq); mmc_wait_for_req_done(host, host->areq->mrq);
err = host->areq->err_check(host->card, host->areq); err = host->areq->err_check(host->card, host->areq);
if (err) { if (err) {
/* post process the completed failed request */
mmc_post_req(host, host->areq->mrq, 0); mmc_post_req(host, host->areq->mrq, 0);
if (areq) if (areq)
/*
* Cancel the new prepared request, because
* it can't run until the failed
* request has been properly handled.
*/
mmc_post_req(host, areq->mrq, -EINVAL); mmc_post_req(host, areq->mrq, -EINVAL);
host->areq = NULL; host->areq = NULL;
@ -318,6 +379,63 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
} }
EXPORT_SYMBOL(mmc_wait_for_req); EXPORT_SYMBOL(mmc_wait_for_req);
/**
* mmc_interrupt_hpi - Issue for High priority Interrupt
* @card: the MMC card associated with the HPI transfer
*
* Issued High Priority Interrupt, and check for card status
* util out-of prg-state.
*/
int mmc_interrupt_hpi(struct mmc_card *card)
{
int err;
u32 status;
BUG_ON(!card);
if (!card->ext_csd.hpi_en) {
pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host));
return 1;
}
mmc_claim_host(card->host);
err = mmc_send_status(card, &status);
if (err) {
pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
goto out;
}
/*
* If the card status is in PRG-state, we can send the HPI command.
*/
if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
do {
/*
* We don't know when the HPI command will finish
* processing, so we need to resend HPI until out
* of prg-state, and keep checking the card status
* with SEND_STATUS. If a timeout error occurs when
* sending the HPI command, we are already out of
* prg-state.
*/
err = mmc_send_hpi_cmd(card, &status);
if (err)
pr_debug("%s: abort HPI (%d error)\n",
mmc_hostname(card->host), err);
err = mmc_send_status(card, &status);
if (err)
break;
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
} else
pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
out:
mmc_release_host(card->host);
return err;
}
EXPORT_SYMBOL(mmc_interrupt_hpi);
/** /**
* mmc_wait_for_cmd - start a command and wait for completion * mmc_wait_for_cmd - start a command and wait for completion
* @host: MMC host to start command * @host: MMC host to start command
@ -330,7 +448,7 @@ EXPORT_SYMBOL(mmc_wait_for_req);
*/ */
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{ {
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
@ -1119,13 +1237,11 @@ static void mmc_power_up(struct mmc_host *host)
bit = fls(host->ocr_avail) - 1; bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit; host->ios.vdd = bit;
if (mmc_host_is_spi(host)) { if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH; host->ios.chip_select = MMC_CS_HIGH;
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; else
} else {
host->ios.chip_select = MMC_CS_DONTCARE; host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
}
host->ios.power_mode = MMC_POWER_UP; host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY; host->ios.timing = MMC_TIMING_LEGACY;
@ -1151,13 +1267,45 @@ static void mmc_power_up(struct mmc_host *host)
mmc_host_clk_release(host); mmc_host_clk_release(host);
} }
static void mmc_power_off(struct mmc_host *host) void mmc_power_off(struct mmc_host *host)
{ {
struct mmc_card *card;
unsigned int notify_type;
unsigned int timeout;
int err;
mmc_host_clk_hold(host); mmc_host_clk_hold(host);
card = host->card;
host->ios.clock = 0; host->ios.clock = 0;
host->ios.vdd = 0; host->ios.vdd = 0;
if (card && mmc_card_mmc(card) &&
(card->poweroff_notify_state == MMC_POWERED_ON)) {
if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
notify_type = EXT_CSD_POWER_OFF_SHORT;
timeout = card->ext_csd.generic_cmd6_time;
card->poweroff_notify_state = MMC_POWEROFF_SHORT;
} else {
notify_type = EXT_CSD_POWER_OFF_LONG;
timeout = card->ext_csd.power_off_longtime;
card->poweroff_notify_state = MMC_POWEROFF_LONG;
}
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout);
if (err && err != -EBADMSG)
pr_err("Device failed to respond within %d poweroff "
"time. Forcefully powering down the device\n",
timeout);
/* Set the card state to no notification after the poweroff */
card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
}
/* /*
* Reset ocr mask to be the highest possible voltage supported for * Reset ocr mask to be the highest possible voltage supported for
* this mmc host. This value will be used at next power up. * this mmc host. This value will be used at next power up.
@ -1173,6 +1321,13 @@ static void mmc_power_off(struct mmc_host *host)
host->ios.timing = MMC_TIMING_LEGACY; host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host); mmc_set_ios(host);
/*
* Some configurations, such as the 802.11 SDIO card in the OLPC
* XO-1.5, require a short delay after poweroff before the card
* can be successfully turned on again.
*/
mmc_delay(1);
mmc_host_clk_release(host); mmc_host_clk_release(host);
} }
@ -1241,8 +1396,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
} }
/* /*
* Remove the current bus handler from a host. Assumes that there are * Remove the current bus handler from a host.
* no interesting cards left, so the bus is powered down.
*/ */
void mmc_detach_bus(struct mmc_host *host) void mmc_detach_bus(struct mmc_host *host)
{ {
@ -1259,8 +1413,6 @@ void mmc_detach_bus(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
mmc_power_off(host);
mmc_bus_put(host); mmc_bus_put(host);
} }
@ -1478,9 +1630,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) { if (err) {
printk(KERN_ERR "mmc_erase: group start error %d, " pr_err("mmc_erase: group start error %d, "
"status %#x\n", err, cmd.resp[0]); "status %#x\n", err, cmd.resp[0]);
err = -EINVAL; err = -EIO;
goto out; goto out;
} }
@ -1493,9 +1645,9 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) { if (err) {
printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n", pr_err("mmc_erase: group end error %d, status %#x\n",
err, cmd.resp[0]); err, cmd.resp[0]);
err = -EINVAL; err = -EIO;
goto out; goto out;
} }
@ -1506,7 +1658,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty); cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) { if (err) {
printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n", pr_err("mmc_erase: erase error %d, status %#x\n",
err, cmd.resp[0]); err, cmd.resp[0]);
err = -EIO; err = -EIO;
goto out; goto out;
@ -1523,7 +1675,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
/* Do not retry else we can't see errors */ /* Do not retry else we can't see errors */
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err || (cmd.resp[0] & 0xFDF92000)) { if (err || (cmd.resp[0] & 0xFDF92000)) {
printk(KERN_ERR "error %d requesting status %#x\n", pr_err("error %d requesting status %#x\n",
err, cmd.resp[0]); err, cmd.resp[0]);
err = -EIO; err = -EIO;
goto out; goto out;
@ -1614,10 +1766,32 @@ int mmc_can_trim(struct mmc_card *card)
{ {
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
return 1; return 1;
if (mmc_can_discard(card))
return 1;
return 0; return 0;
} }
EXPORT_SYMBOL(mmc_can_trim); EXPORT_SYMBOL(mmc_can_trim);
int mmc_can_discard(struct mmc_card *card)
{
/*
* As there's no way to detect the discard support bit at v4.5
* use the s/w feature support filed.
*/
if (card->ext_csd.feature_support & MMC_DISCARD_FEATURE)
return 1;
return 0;
}
EXPORT_SYMBOL(mmc_can_discard);
int mmc_can_sanitize(struct mmc_card *card)
{
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE)
return 1;
return 0;
}
EXPORT_SYMBOL(mmc_can_sanitize);
int mmc_can_secure_erase_trim(struct mmc_card *card) int mmc_can_secure_erase_trim(struct mmc_card *card)
{ {
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
@ -1727,6 +1901,94 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
} }
EXPORT_SYMBOL(mmc_set_blocklen); EXPORT_SYMBOL(mmc_set_blocklen);
static void mmc_hw_reset_for_init(struct mmc_host *host)
{
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
return;
mmc_host_clk_hold(host);
host->ops->hw_reset(host);
mmc_host_clk_release(host);
}
int mmc_can_reset(struct mmc_card *card)
{
u8 rst_n_function;
if (!mmc_card_mmc(card))
return 0;
rst_n_function = card->ext_csd.rst_n_function;
if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
return 0;
return 1;
}
EXPORT_SYMBOL(mmc_can_reset);
static int mmc_do_hw_reset(struct mmc_host *host, int check)
{
struct mmc_card *card = host->card;
if (!host->bus_ops->power_restore)
return -EOPNOTSUPP;
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
return -EOPNOTSUPP;
if (!card)
return -EINVAL;
if (!mmc_can_reset(card))
return -EOPNOTSUPP;
mmc_host_clk_hold(host);
mmc_set_clock(host, host->f_init);
host->ops->hw_reset(host);
/* If the reset has happened, then a status command will fail */
if (check) {
struct mmc_command cmd = {0};
int err;
cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (!err) {
mmc_host_clk_release(host);
return -ENOSYS;
}
}
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR);
if (mmc_host_is_spi(host)) {
host->ios.chip_select = MMC_CS_HIGH;
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
} else {
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
}
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host);
mmc_host_clk_release(host);
return host->bus_ops->power_restore(host);
}
int mmc_hw_reset(struct mmc_host *host)
{
return mmc_do_hw_reset(host, 0);
}
EXPORT_SYMBOL(mmc_hw_reset);
int mmc_hw_reset_check(struct mmc_host *host)
{
return mmc_do_hw_reset(host, 1);
}
EXPORT_SYMBOL(mmc_hw_reset_check);
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{ {
host->f_init = freq; host->f_init = freq;
@ -1737,6 +1999,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
#endif #endif
mmc_power_up(host); mmc_power_up(host);
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host);
/* /*
* sdio_reset sends CMD52 to reset card. Since we do not know * sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52 * if the card is being re-initialized, just send it. CMD52
@ -1845,6 +2113,7 @@ void mmc_stop_host(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host); mmc_release_host(host);
mmc_bus_put(host); mmc_bus_put(host);
return; return;
@ -1946,6 +2215,65 @@ int mmc_card_can_sleep(struct mmc_host *host)
} }
EXPORT_SYMBOL(mmc_card_can_sleep); EXPORT_SYMBOL(mmc_card_can_sleep);
/*
* Flush the cache to the non-volatile storage.
*/
int mmc_flush_cache(struct mmc_card *card)
{
struct mmc_host *host = card->host;
int err = 0;
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
return err;
if (mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0) &&
(card->ext_csd.cache_ctrl & 1)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_FLUSH_CACHE, 1, 0);
if (err)
pr_err("%s: cache flush error %d\n",
mmc_hostname(card->host), err);
}
return err;
}
EXPORT_SYMBOL(mmc_flush_cache);
/*
* Turn the cache ON/OFF.
* Turning the cache OFF shall trigger flushing of the data
* to the non-volatile storage.
*/
int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
{
struct mmc_card *card = host->card;
int err = 0;
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
mmc_card_is_removable(host))
return err;
if (card && mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0)) {
enable = !!enable;
if (card->ext_csd.cache_ctrl ^ enable)
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_CACHE_CTRL, enable, 0);
if (err)
pr_err("%s: cache %s error %d\n",
mmc_hostname(card->host),
enable ? "on" : "off",
err);
else
card->ext_csd.cache_ctrl = enable;
}
return err;
}
EXPORT_SYMBOL(mmc_cache_ctrl);
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** /**
@ -1960,23 +2288,39 @@ int mmc_suspend_host(struct mmc_host *host)
cancel_delayed_work(&host->disable); cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect); cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work(); mmc_flush_scheduled_work();
err = mmc_cache_ctrl(host, 0);
if (err)
goto out;
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend)
err = host->bus_ops->suspend(host); /*
if (err == -ENOSYS || !host->bus_ops->resume) { * A long response time is not acceptable for device drivers
/* * when doing suspend. Prevent mmc_claim_host in the suspend
* We simply "remove" the card in this case. * sequence, to potentially wait "forever" by trying to
* It will be redetected on resume. * pre-claim the host.
*/ */
if (host->bus_ops->remove) if (mmc_try_claim_host(host)) {
host->bus_ops->remove(host); if (host->bus_ops->suspend)
mmc_claim_host(host); err = host->bus_ops->suspend(host);
mmc_detach_bus(host); if (err == -ENOSYS || !host->bus_ops->resume) {
mmc_release_host(host); /*
host->pm_flags = 0; * We simply "remove" the card in this case.
err = 0; * It will be redetected on resume.
*/
if (host->bus_ops->remove)
host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host);
host->pm_flags = 0;
err = 0;
}
mmc_do_release_host(host);
} else {
err = -EBUSY;
} }
} }
mmc_bus_put(host); mmc_bus_put(host);
@ -1984,6 +2328,7 @@ int mmc_suspend_host(struct mmc_host *host)
if (!err && !mmc_card_keep_power(host)) if (!err && !mmc_card_keep_power(host))
mmc_power_off(host); mmc_power_off(host);
out:
return err; return err;
} }
@ -2018,7 +2363,7 @@ int mmc_resume_host(struct mmc_host *host)
BUG_ON(!host->bus_ops->resume); BUG_ON(!host->bus_ops->resume);
err = host->bus_ops->resume(host); err = host->bus_ops->resume(host);
if (err) { if (err) {
printk(KERN_WARNING "%s: error %d during resume " pr_warning("%s: error %d during resume "
"(card was removed?)\n", "(card was removed?)\n",
mmc_hostname(host), err); mmc_hostname(host), err);
err = 0; err = 0;
@ -2049,6 +2394,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1; host->rescan_disable = 1;
host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect); cancel_delayed_work_sync(&host->detect);
@ -2061,6 +2407,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
host->bus_ops->remove(host); host->bus_ops->remove(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host); mmc_release_host(host);
host->pm_flags = 0; host->pm_flags = 0;
break; break;
@ -2071,6 +2418,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0; host->rescan_disable = 0;
host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0); mmc_detect_change(host, 0);

View File

@ -43,6 +43,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
bool cmd11); bool cmd11);
void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
void mmc_power_off(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms) static inline void mmc_delay(unsigned int ms)
{ {

View File

@ -7,11 +7,13 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/moduleparam.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/fault-inject.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
@ -19,6 +21,14 @@
#include "core.h" #include "core.h"
#include "mmc_ops.h" #include "mmc_ops.h"
#ifdef CONFIG_FAIL_MMC_REQUEST
static DECLARE_FAULT_ATTR(fail_default_attr);
static char *fail_request;
module_param(fail_request, charp, 0);
#endif /* CONFIG_FAIL_MMC_REQUEST */
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ /* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
static int mmc_ios_show(struct seq_file *s, void *data) static int mmc_ios_show(struct seq_file *s, void *data)
{ {
@ -113,6 +123,15 @@ static int mmc_ios_show(struct seq_file *s, void *data)
case MMC_TIMING_SD_HS: case MMC_TIMING_SD_HS:
str = "sd high-speed"; str = "sd high-speed";
break; break;
case MMC_TIMING_UHS_SDR50:
str = "sd uhs SDR50";
break;
case MMC_TIMING_UHS_SDR104:
str = "sd uhs SDR104";
break;
case MMC_TIMING_UHS_DDR50:
str = "sd uhs DDR50";
break;
default: default:
str = "invalid"; str = "invalid";
break; break;
@ -187,6 +206,15 @@ void mmc_add_host_debugfs(struct mmc_host *host)
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
root, &host->clk_delay)) root, &host->clk_delay))
goto err_node; goto err_node;
#endif
#ifdef CONFIG_FAIL_MMC_REQUEST
if (fail_request)
setup_fault_attr(&fail_default_attr, fail_request);
host->fail_mmc_request = fail_default_attr;
if (IS_ERR(fault_create_debugfs_attr("fail_mmc_request",
root,
&host->fail_mmc_request)))
goto err_node;
#endif #endif
return; return;

View File

@ -301,6 +301,17 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_size = 512; host->max_blk_size = 512;
host->max_blk_count = PAGE_CACHE_SIZE / 512; host->max_blk_count = PAGE_CACHE_SIZE / 512;
/*
* Enable runtime power management by default. This flag was added due
* to runtime power management causing disruption for some users, but
* the power on/off code has been improved since then.
*
* We'll enable this flag by default as an experiment, and if no
* problems are reported, we will follow up later and remove the flag
* altogether.
*/
host->caps = MMC_CAP_POWER_OFF_CARD;
return host; return host;
free: free:

View File

@ -101,7 +101,7 @@ static int mmc_decode_cid(struct mmc_card *card)
break; break;
default: default:
printk(KERN_ERR "%s: card has unknown MMCA version %d\n", pr_err("%s: card has unknown MMCA version %d\n",
mmc_hostname(card->host), card->csd.mmca_vsn); mmc_hostname(card->host), card->csd.mmca_vsn);
return -EINVAL; return -EINVAL;
} }
@ -135,7 +135,7 @@ static int mmc_decode_csd(struct mmc_card *card)
*/ */
csd->structure = UNSTUFF_BITS(resp, 126, 2); csd->structure = UNSTUFF_BITS(resp, 126, 2);
if (csd->structure == 0) { if (csd->structure == 0) {
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", pr_err("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd->structure); mmc_hostname(card->host), csd->structure);
return -EINVAL; return -EINVAL;
} }
@ -195,7 +195,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
*/ */
ext_csd = kmalloc(512, GFP_KERNEL); ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) { if (!ext_csd) {
printk(KERN_ERR "%s: could not allocate a buffer to " pr_err("%s: could not allocate a buffer to "
"receive the ext_csd.\n", mmc_hostname(card->host)); "receive the ext_csd.\n", mmc_hostname(card->host));
return -ENOMEM; return -ENOMEM;
} }
@ -217,12 +217,12 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
* stored in their CSD. * stored in their CSD.
*/ */
if (card->csd.capacity == (4096 * 512)) { if (card->csd.capacity == (4096 * 512)) {
printk(KERN_ERR "%s: unable to read EXT_CSD " pr_err("%s: unable to read EXT_CSD "
"on a possible high capacity card. " "on a possible high capacity card. "
"Card will be ignored.\n", "Card will be ignored.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
} else { } else {
printk(KERN_WARNING "%s: unable to read " pr_warning("%s: unable to read "
"EXT_CSD, performance might " "EXT_CSD, performance might "
"suffer.\n", "suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
@ -239,7 +239,9 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
*/ */
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
{ {
int err = 0; int err = 0, idx;
unsigned int part_size;
u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0;
BUG_ON(!card); BUG_ON(!card);
@ -250,7 +252,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE]; card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
if (card->csd.structure == 3) { if (card->csd.structure == 3) {
if (card->ext_csd.raw_ext_csd_structure > 2) { if (card->ext_csd.raw_ext_csd_structure > 2) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure " pr_err("%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host), "version %d\n", mmc_hostname(card->host),
card->ext_csd.raw_ext_csd_structure); card->ext_csd.raw_ext_csd_structure);
err = -EINVAL; err = -EINVAL;
@ -260,7 +262,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.rev = ext_csd[EXT_CSD_REV]; card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 6) { if (card->ext_csd.rev > 6) {
printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n", pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev); mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL; err = -EINVAL;
goto out; goto out;
@ -306,7 +308,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
break; break;
default: default:
/* MMC v4 spec says this cannot happen */ /* MMC v4 spec says this cannot happen */
printk(KERN_WARNING "%s: card is mmc v4 but doesn't " pr_warning("%s: card is mmc v4 but doesn't "
"support any high-speed modes.\n", "support any high-speed modes.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
} }
@ -340,7 +342,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
* There are two boot regions of equal size, defined in * There are two boot regions of equal size, defined in
* multiples of 128K. * multiples of 128K.
*/ */
card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) {
for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
mmc_part_add(card, part_size,
EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
"boot%d", idx, true);
}
}
} }
card->ext_csd.raw_hc_erase_gap_size = card->ext_csd.raw_hc_erase_gap_size =
@ -359,11 +368,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
* card has the Enhanced area enabled. If so, export enhanced * card has the Enhanced area enabled. If so, export enhanced
* area offset and size to user by adding sysfs interface. * area offset and size to user by adding sysfs interface.
*/ */
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
u8 hc_erase_grp_sz = hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
u8 hc_wp_grp_sz = hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
card->ext_csd.enhanced_area_en = 1; card->ext_csd.enhanced_area_en = 1;
@ -392,6 +402,41 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.enhanced_area_offset = -EINVAL; card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL; card->ext_csd.enhanced_area_size = -EINVAL;
} }
/*
* General purpose partition feature support --
* If ext_csd has the size of general purpose partitions,
* set size, part_cfg, partition name in mmc_part.
*/
if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
EXT_CSD_PART_SUPPORT_PART_EN) {
if (card->ext_csd.enhanced_area_en != 1) {
hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
card->ext_csd.enhanced_area_en = 1;
}
for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
continue;
part_size =
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
<< 16) +
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
<< 8) +
ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
part_size *= (size_t)(hc_erase_grp_sz *
hc_wp_grp_sz);
mmc_part_add(card, part_size << 19,
EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
"gp%d", idx, false);
}
}
card->ext_csd.sec_trim_mult = card->ext_csd.sec_trim_mult =
ext_csd[EXT_CSD_SEC_TRIM_MULT]; ext_csd[EXT_CSD_SEC_TRIM_MULT];
card->ext_csd.sec_erase_mult = card->ext_csd.sec_erase_mult =
@ -402,14 +447,48 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_TRIM_MULT]; ext_csd[EXT_CSD_TRIM_MULT];
} }
if (card->ext_csd.rev >= 5) if (card->ext_csd.rev >= 5) {
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; /* check whether the eMMC card supports HPI */
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
card->ext_csd.hpi = 1;
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
else
card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
/*
* Indicate the maximum timeout to close
* a command interrupted by HPI
*/
card->ext_csd.out_of_int_time =
ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
}
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION];
}
card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
card->erased_byte = 0xFF; card->erased_byte = 0xFF;
else else
card->erased_byte = 0x0; card->erased_byte = 0x0;
/* eMMC v4.5 or later */
if (card->ext_csd.rev >= 6) {
card->ext_csd.feature_support |= MMC_DISCARD_FEATURE;
card->ext_csd.generic_cmd6_time = 10 *
ext_csd[EXT_CSD_GENERIC_CMD6_TIME];
card->ext_csd.power_off_longtime = 10 *
ext_csd[EXT_CSD_POWER_OFF_LONG_TIME];
card->ext_csd.cache_size =
ext_csd[EXT_CSD_CACHE_SIZE + 0] << 0 |
ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 |
ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24;
}
out: out:
return err; return err;
} }
@ -529,6 +608,86 @@ static struct device_type mmc_type = {
.groups = mmc_attr_groups, .groups = mmc_attr_groups,
}; };
/*
* Select the PowerClass for the current bus width
* If power class is defined for 4/8 bit bus in the
* extended CSD register, select it by executing the
* mmc_switch command.
*/
static int mmc_select_powerclass(struct mmc_card *card,
unsigned int bus_width, u8 *ext_csd)
{
int err = 0;
unsigned int pwrclass_val;
unsigned int index = 0;
struct mmc_host *host;
BUG_ON(!card);
host = card->host;
BUG_ON(!host);
if (ext_csd == NULL)
return 0;
/* Power class selection is supported for versions >= 4.0 */
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0;
/* Power class values are defined only for 4/8 bit bus */
if (bus_width == EXT_CSD_BUS_WIDTH_1)
return 0;
switch (1 << host->ios.vdd) {
case MMC_VDD_165_195:
if (host->ios.clock <= 26000000)
index = EXT_CSD_PWR_CL_26_195;
else if (host->ios.clock <= 52000000)
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
EXT_CSD_PWR_CL_52_195 :
EXT_CSD_PWR_CL_DDR_52_195;
else if (host->ios.clock <= 200000000)
index = EXT_CSD_PWR_CL_200_195;
break;
case MMC_VDD_32_33:
case MMC_VDD_33_34:
case MMC_VDD_34_35:
case MMC_VDD_35_36:
if (host->ios.clock <= 26000000)
index = EXT_CSD_PWR_CL_26_360;
else if (host->ios.clock <= 52000000)
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
EXT_CSD_PWR_CL_52_360 :
EXT_CSD_PWR_CL_DDR_52_360;
else if (host->ios.clock <= 200000000)
index = EXT_CSD_PWR_CL_200_360;
break;
default:
pr_warning("%s: Voltage range not supported "
"for power class.\n", mmc_hostname(host));
return -EINVAL;
}
pwrclass_val = ext_csd[index];
if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8))
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >>
EXT_CSD_PWR_CL_8BIT_SHIFT;
else
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_4BIT_MASK) >>
EXT_CSD_PWR_CL_4BIT_SHIFT;
/* If the power class is different from the default value */
if (pwrclass_val > 0) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_CLASS,
pwrclass_val,
card->ext_csd.generic_cmd6_time);
}
return err;
}
/* /*
* Handle the detection and initialisation of a card. * Handle the detection and initialisation of a card.
* *
@ -548,11 +707,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
/* Set correct bus mode for MMC before attempting init */
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
/* /*
* Since we're changing the OCR value, we seem to * Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle * need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to * state. We wait 1ms to give cards time to
* respond. * respond.
* mmc_go_idle is needed for eMMC that are asleep
*/ */
mmc_go_idle(host); mmc_go_idle(host);
@ -668,7 +832,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/ */
if (card->ext_csd.enhanced_area_en) { if (card->ext_csd.enhanced_area_en) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1, 0); EXT_CSD_ERASE_GROUP_DEF, 1,
card->ext_csd.generic_cmd6_time);
if (err && err != -EBADMSG) if (err && err != -EBADMSG)
goto free_card; goto free_card;
@ -705,18 +870,36 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
goto free_card; goto free_card;
} }
/*
* If the host supports the power_off_notify capability then
* set the notification byte in the ext_csd register of device
*/
if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
(card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
EXT_CSD_POWER_ON,
card->ext_csd.generic_cmd6_time);
if (err && err != -EBADMSG)
goto free_card;
}
if (!err)
card->poweroff_notify_state = MMC_POWERED_ON;
/* /*
* Activate high speed (if supported) * Activate high speed (if supported)
*/ */
if ((card->ext_csd.hs_max_dtr != 0) && if ((card->ext_csd.hs_max_dtr != 0) &&
(host->caps & MMC_CAP_MMC_HIGHSPEED)) { (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 1, 0); EXT_CSD_HS_TIMING, 1,
card->ext_csd.generic_cmd6_time);
if (err && err != -EBADMSG) if (err && err != -EBADMSG)
goto free_card; goto free_card;
if (err) { if (err) {
printk(KERN_WARNING "%s: switch to highspeed failed\n", pr_warning("%s: switch to highspeed failed\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
} else { } else {
@ -725,6 +908,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
} }
/*
* Enable HPI feature (if supported)
*/
if (card->ext_csd.hpi) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HPI_MGMT, 1, 0);
if (err && err != -EBADMSG)
goto free_card;
if (err) {
pr_warning("%s: Enabling HPI failed\n",
mmc_hostname(card->host));
err = 0;
} else
card->ext_csd.hpi_en = 1;
}
/* /*
* Compute bus speed. * Compute bus speed.
*/ */
@ -780,10 +979,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
bus_width = bus_widths[idx]; bus_width = bus_widths[idx];
if (bus_width == MMC_BUS_WIDTH_1) if (bus_width == MMC_BUS_WIDTH_1)
ddr = 0; /* no DDR for 1-bit width */ ddr = 0; /* no DDR for 1-bit width */
err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
ext_csd);
if (err)
pr_err("%s: power class selection to "
"bus width %d failed\n",
mmc_hostname(card->host),
1 << bus_width);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx][0], ext_csd_bits[idx][0],
0); card->ext_csd.generic_cmd6_time);
if (!err) { if (!err) {
mmc_set_bus_width(card->host, bus_width); mmc_set_bus_width(card->host, bus_width);
@ -803,13 +1010,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
if (!err && ddr) { if (!err && ddr) {
err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
ext_csd);
if (err)
pr_err("%s: power class selection to "
"bus width %d ddr %d failed\n",
mmc_hostname(card->host),
1 << bus_width, ddr);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx][1], ext_csd_bits[idx][1],
0); card->ext_csd.generic_cmd6_time);
} }
if (err) { if (err) {
printk(KERN_WARNING "%s: switch to bus width %d ddr %d " pr_warning("%s: switch to bus width %d ddr %d "
"failed\n", mmc_hostname(card->host), "failed\n", mmc_hostname(card->host),
1 << bus_width, ddr); 1 << bus_width, ddr);
goto free_card; goto free_card;
@ -840,6 +1055,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
} }
/*
* If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on.
*/
if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
card->ext_csd.cache_size > 0) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_CACHE_CTRL, 1, 0);
if (err && err != -EBADMSG)
goto free_card;
/*
* Only if no error, cache is turned on successfully.
*/
card->ext_csd.cache_ctrl = err ? 0 : 1;
}
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
@ -891,6 +1123,7 @@ static void mmc_detect(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host); mmc_release_host(host);
} }
} }
@ -900,16 +1133,20 @@ static void mmc_detect(struct mmc_host *host)
*/ */
static int mmc_suspend(struct mmc_host *host) static int mmc_suspend(struct mmc_host *host)
{ {
int err = 0;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
if (!mmc_host_is_spi(host)) if (mmc_card_can_sleep(host))
err = mmc_card_sleep(host);
else if (!mmc_host_is_spi(host))
mmc_deselect_cards(host); mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
return 0; return err;
} }
/* /*
@ -1016,6 +1253,10 @@ int mmc_attach_mmc(struct mmc_host *host)
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
/* Set correct bus mode for MMC before attempting attach */
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
err = mmc_send_op_cond(host, 0, &ocr); err = mmc_send_op_cond(host, 0, &ocr);
if (err) if (err)
return err; return err;
@ -1038,7 +1279,7 @@ int mmc_attach_mmc(struct mmc_host *host)
* support. * support.
*/ */
if (ocr & 0x7F) { if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages " pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n", "below the defined range. These will be ignored.\n",
mmc_hostname(host)); mmc_hostname(host));
ocr &= ~0x7F; ocr &= ~0x7F;
@ -1077,7 +1318,7 @@ remove_card:
err: err:
mmc_detach_bus(host); mmc_detach_bus(host);
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", pr_err("%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
return err; return err;

View File

@ -233,7 +233,7 @@ static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len) u32 opcode, void *buf, unsigned len)
{ {
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
@ -414,7 +414,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
return -EBADMSG; return -EBADMSG;
} else { } else {
if (status & 0xFDFFA000) if (status & 0xFDFFA000)
printk(KERN_WARNING "%s: unexpected status %#x after " pr_warning("%s: unexpected status %#x after "
"switch", mmc_hostname(card->host), status); "switch", mmc_hostname(card->host), status);
if (status & R1_SWITCH_ERROR) if (status & R1_SWITCH_ERROR)
return -EBADMSG; return -EBADMSG;
@ -454,7 +454,7 @@ static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len) u8 len)
{ {
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
@ -476,7 +476,7 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
else if (len == 4) else if (len == 4)
test_buf = testdata_4bit; test_buf = testdata_4bit;
else { else {
printk(KERN_ERR "%s: Invalid bus_width %d\n", pr_err("%s: Invalid bus_width %d\n",
mmc_hostname(host), len); mmc_hostname(host), len);
kfree(data_buf); kfree(data_buf);
return -EINVAL; return -EINVAL;
@ -547,3 +547,34 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width); err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
return err; return err;
} }
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
{
struct mmc_command cmd = {0};
unsigned int opcode;
unsigned int flags;
int err;
opcode = card->ext_csd.hpi_cmd;
if (opcode == MMC_STOP_TRANSMISSION)
flags = MMC_RSP_R1 | MMC_CMD_AC;
else if (opcode == MMC_SEND_STATUS)
flags = MMC_RSP_R1 | MMC_CMD_AC;
cmd.opcode = opcode;
cmd.arg = card->rca << 16 | 1;
cmd.flags = flags;
cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
pr_warn("%s: error %d interrupting operation. "
"HPI command response %#x\n", mmc_hostname(card->host),
err, cmd.resp[0]);
return err;
}
if (status)
*status = cmd.resp[0];
return 0;
}

View File

@ -26,6 +26,7 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep); int mmc_card_sleepawake(struct mmc_host *host, int sleep);
int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
#endif #endif

View File

@ -21,6 +21,14 @@
#define SDIO_DEVICE_ID_TI_WL1271 0x4076 #define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif #endif
#ifndef SDIO_VENDOR_ID_STE
#define SDIO_VENDOR_ID_STE 0x0020
#endif
#ifndef SDIO_DEVICE_ID_STE_CW1200
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#endif
/* /*
* This hook just adds a quirk for all sdio devices * This hook just adds a quirk for all sdio devices
*/ */
@ -46,6 +54,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_DISABLE_CD), add_quirk, MMC_QUIRK_DISABLE_CD),
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
END_FIXUP END_FIXUP
}; };

View File

@ -163,7 +163,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->erase_size = 1; csd->erase_size = 1;
break; break;
default: default:
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", pr_err("%s: unrecognised CSD structure version %d\n",
mmc_hostname(card->host), csd_struct); mmc_hostname(card->host), csd_struct);
return -EINVAL; return -EINVAL;
} }
@ -187,7 +187,7 @@ static int mmc_decode_scr(struct mmc_card *card)
scr_struct = UNSTUFF_BITS(resp, 60, 4); scr_struct = UNSTUFF_BITS(resp, 60, 4);
if (scr_struct != 0) { if (scr_struct != 0) {
printk(KERN_ERR "%s: unrecognised SCR structure version %d\n", pr_err("%s: unrecognised SCR structure version %d\n",
mmc_hostname(card->host), scr_struct); mmc_hostname(card->host), scr_struct);
return -EINVAL; return -EINVAL;
} }
@ -218,7 +218,7 @@ static int mmc_read_ssr(struct mmc_card *card)
u32 *ssr; u32 *ssr;
if (!(card->csd.cmdclass & CCC_APP_SPEC)) { if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
printk(KERN_WARNING "%s: card lacks mandatory SD Status " pr_warning("%s: card lacks mandatory SD Status "
"function.\n", mmc_hostname(card->host)); "function.\n", mmc_hostname(card->host));
return 0; return 0;
} }
@ -229,7 +229,7 @@ static int mmc_read_ssr(struct mmc_card *card)
err = mmc_app_sd_status(card, ssr); err = mmc_app_sd_status(card, ssr);
if (err) { if (err) {
printk(KERN_WARNING "%s: problem reading SD Status " pr_warning("%s: problem reading SD Status "
"register.\n", mmc_hostname(card->host)); "register.\n", mmc_hostname(card->host));
err = 0; err = 0;
goto out; goto out;
@ -253,7 +253,7 @@ static int mmc_read_ssr(struct mmc_card *card)
card->ssr.erase_offset = eo * 1000; card->ssr.erase_offset = eo * 1000;
} }
} else { } else {
printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit " pr_warning("%s: SD Status: Invalid Allocation Unit "
"size.\n", mmc_hostname(card->host)); "size.\n", mmc_hostname(card->host));
} }
out: out:
@ -273,7 +273,7 @@ static int mmc_read_switch(struct mmc_card *card)
return 0; return 0;
if (!(card->csd.cmdclass & CCC_SWITCH)) { if (!(card->csd.cmdclass & CCC_SWITCH)) {
printk(KERN_WARNING "%s: card lacks mandatory switch " pr_warning("%s: card lacks mandatory switch "
"function, performance might suffer.\n", "function, performance might suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return 0; return 0;
@ -283,7 +283,7 @@ static int mmc_read_switch(struct mmc_card *card)
status = kmalloc(64, GFP_KERNEL); status = kmalloc(64, GFP_KERNEL);
if (!status) { if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for " pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", "switch capabilities.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return -ENOMEM; return -ENOMEM;
@ -299,13 +299,16 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out; goto out;
printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n", pr_warning("%s: problem reading Bus Speed modes.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
goto out; goto out;
} }
if (status[13] & UHS_SDR50_BUS_SPEED)
card->sw_caps.hs_max_dtr = 50000000;
if (card->scr.sda_spec3) { if (card->scr.sda_spec3) {
card->sw_caps.sd3_bus_mode = status[13]; card->sw_caps.sd3_bus_mode = status[13];
@ -319,7 +322,7 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out; goto out;
printk(KERN_WARNING "%s: problem reading " pr_warning("%s: problem reading "
"Driver Strength.\n", "Driver Strength.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
@ -339,7 +342,7 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out; goto out;
printk(KERN_WARNING "%s: problem reading " pr_warning("%s: problem reading "
"Current Limit.\n", "Current Limit.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
@ -348,9 +351,6 @@ static int mmc_read_switch(struct mmc_card *card)
} }
card->sw_caps.sd3_curr_limit = status[7]; card->sw_caps.sd3_curr_limit = status[7];
} else {
if (status[13] & 0x02)
card->sw_caps.hs_max_dtr = 50000000;
} }
out: out:
@ -383,7 +383,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
status = kmalloc(64, GFP_KERNEL); status = kmalloc(64, GFP_KERNEL);
if (!status) { if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for " pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host)); "switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM; return -ENOMEM;
} }
@ -393,7 +393,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
goto out; goto out;
if ((status[16] & 0xF) != 1) { if ((status[16] & 0xF) != 1) {
printk(KERN_WARNING "%s: Problem switching card " pr_warning("%s: Problem switching card "
"into high-speed mode!\n", "into high-speed mode!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
@ -459,7 +459,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
return err; return err;
if ((status[15] & 0xF) != drive_strength) { if ((status[15] & 0xF) != drive_strength) {
printk(KERN_WARNING "%s: Problem setting drive strength!\n", pr_warning("%s: Problem setting drive strength!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return 0; return 0;
} }
@ -538,7 +538,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
return err; return err;
if ((status[16] & 0xF) != card->sd_bus_speed) if ((status[16] & 0xF) != card->sd_bus_speed)
printk(KERN_WARNING "%s: Problem setting bus speed mode!\n", pr_warning("%s: Problem setting bus speed mode!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
else { else {
mmc_set_timing(card->host, timing); mmc_set_timing(card->host, timing);
@ -600,7 +600,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
return err; return err;
if (((status[15] >> 4) & 0x0F) != current_limit) if (((status[15] >> 4) & 0x0F) != current_limit)
printk(KERN_WARNING "%s: Problem setting current limit!\n", pr_warning("%s: Problem setting current limit!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return 0; return 0;
@ -622,7 +622,7 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
status = kmalloc(64, GFP_KERNEL); status = kmalloc(64, GFP_KERNEL);
if (!status) { if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for " pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host)); "switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM; return -ENOMEM;
} }
@ -852,7 +852,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
ro = host->ops->get_ro(host); ro = host->ops->get_ro(host);
if (ro < 0) { if (ro < 0) {
printk(KERN_WARNING "%s: host does not " pr_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));
@ -929,8 +929,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_relative_addr(host, &card->rca); err = mmc_send_relative_addr(host, &card->rca);
if (err) if (err)
return err; return err;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
} }
if (!oldcard) { if (!oldcard) {
@ -1043,6 +1041,7 @@ static void mmc_sd_detect(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host); mmc_release_host(host);
} }
} }
@ -1167,7 +1166,7 @@ int mmc_attach_sd(struct mmc_host *host)
* support. * support.
*/ */
if (ocr & 0x7F) { if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages " pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n", "below the defined range. These will be ignored.\n",
mmc_hostname(host)); mmc_hostname(host));
ocr &= ~0x7F; ocr &= ~0x7F;
@ -1175,7 +1174,7 @@ int mmc_attach_sd(struct mmc_host *host)
if ((ocr & MMC_VDD_165_195) && if ((ocr & MMC_VDD_165_195) &&
!(host->ocr_avail_sd & MMC_VDD_165_195)) { !(host->ocr_avail_sd & MMC_VDD_165_195)) {
printk(KERN_WARNING "%s: SD card claims to support the " pr_warning("%s: SD card claims to support the "
"incompletely defined 'low voltage range'. This " "incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host)); "will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195; ocr &= ~MMC_VDD_165_195;
@ -1214,7 +1213,7 @@ remove_card:
err: err:
mmc_detach_bus(host); mmc_detach_bus(host);
printk(KERN_ERR "%s: error %d whilst initialising SD card\n", pr_err("%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
return err; return err;

View File

@ -67,7 +67,7 @@ EXPORT_SYMBOL_GPL(mmc_app_cmd);
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries) struct mmc_command *cmd, int retries)
{ {
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
int i, err; int i, err;
@ -244,7 +244,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
int mmc_app_send_scr(struct mmc_card *card, u32 *scr) int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
{ {
int err; int err;
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
@ -303,7 +303,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
int mmc_sd_switch(struct mmc_card *card, int mode, int group, int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp) u8 value, u8 *resp)
{ {
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
@ -348,7 +348,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
int mmc_app_sd_status(struct mmc_card *card, void *ssr) int mmc_app_sd_status(struct mmc_card *card, void *ssr)
{ {
int err; int err;
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;

View File

@ -111,8 +111,8 @@ static int sdio_read_cccr(struct mmc_card *card)
cccr_vsn = data & 0x0f; cccr_vsn = data & 0x0f;
if (cccr_vsn > SDIO_CCCR_REV_1_20) { if (cccr_vsn > SDIO_CCCR_REV_3_00) {
printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n", pr_err("%s: unrecognised CCCR structure version %d\n",
mmc_hostname(card->host), cccr_vsn); mmc_hostname(card->host), cccr_vsn);
return -EINVAL; return -EINVAL;
} }
@ -408,8 +408,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
*/ */
if (oldcard) if (oldcard)
oldcard->rca = card->rca; oldcard->rca = card->rca;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
} }
/* /*
@ -597,6 +595,7 @@ out:
mmc_claim_host(host); mmc_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host); mmc_release_host(host);
} }
} }
@ -778,7 +777,7 @@ int mmc_attach_sdio(struct mmc_host *host)
* support. * support.
*/ */
if (ocr & 0x7F) { if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages " pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n", "below the defined range. These will be ignored.\n",
mmc_hostname(host)); mmc_hostname(host));
ocr &= ~0x7F; ocr &= ~0x7F;
@ -875,7 +874,7 @@ remove:
err: err:
mmc_detach_bus(host); mmc_detach_bus(host);
printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", pr_err("%s: error %d whilst initialising SDIO card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
return err; return err;

View File

@ -173,7 +173,7 @@ static int sdio_bus_remove(struct device *dev)
drv->remove(func); drv->remove(func);
if (func->irq_handler) { if (func->irq_handler) {
printk(KERN_WARNING "WARNING: driver %s did not remove " pr_warning("WARNING: driver %s did not remove "
"its interrupt handler!\n", drv->name); "its interrupt handler!\n", drv->name);
sdio_claim_host(func); sdio_claim_host(func);
sdio_release_irq(func); sdio_release_irq(func);

View File

@ -132,7 +132,7 @@ static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
ret = -EINVAL; ret = -EINVAL;
} }
if (ret && ret != -EILSEQ && ret != -ENOENT) { if (ret && ret != -EILSEQ && ret != -ENOENT) {
printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n", pr_err("%s: bad %s tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host), tpl_descr, code, size); mmc_hostname(card->host), tpl_descr, code, size);
} }
} else { } else {
@ -313,7 +313,7 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
if (ret == -ENOENT) { if (ret == -ENOENT) {
/* warn about unknown tuples */ /* warn about unknown tuples */
printk(KERN_WARNING "%s: queuing unknown" pr_warning("%s: queuing unknown"
" CIS tuple 0x%02x (%u bytes)\n", " CIS tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host), mmc_hostname(card->host),
tpl_code, tpl_link); tpl_code, tpl_link);

View File

@ -45,7 +45,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
if (ret) { if (ret) {
printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", pr_debug("%s: error %d reading SDIO_CCCR_INTx\n",
mmc_card_id(card), ret); mmc_card_id(card), ret);
return ret; return ret;
} }
@ -55,7 +55,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
if (pending & (1 << i)) { if (pending & (1 << i)) {
func = card->sdio_func[i - 1]; func = card->sdio_func[i - 1];
if (!func) { if (!func) {
printk(KERN_WARNING "%s: pending IRQ for " pr_warning("%s: pending IRQ for "
"non-existent function\n", "non-existent function\n",
mmc_card_id(card)); mmc_card_id(card));
ret = -EINVAL; ret = -EINVAL;
@ -63,7 +63,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
func->irq_handler(func); func->irq_handler(func);
count++; count++;
} else { } else {
printk(KERN_WARNING "%s: pending IRQ with no handler\n", pr_warning("%s: pending IRQ with no handler\n",
sdio_func_id(func)); sdio_func_id(func));
ret = -EINVAL; ret = -EINVAL;
} }

View File

@ -121,7 +121,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{ {
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
@ -144,8 +144,11 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
cmd.arg |= fn << 28; cmd.arg |= fn << 28;
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
cmd.arg |= addr << 9; cmd.arg |= addr << 9;
if (blocks == 1 && blksz <= 512) if (blocks == 1 && blksz < 512)
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ cmd.arg |= blksz; /* byte mode */
else if (blocks == 1 && blksz == 512 &&
!(mmc_card_broken_byte_mode_512(card)))
cmd.arg |= 0; /* byte mode, 0==512 */
else else
cmd.arg |= 0x08000000 | blocks; /* block mode */ cmd.arg |= 0x08000000 | blocks; /* block mode */
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;

View File

@ -130,13 +130,13 @@ config MMC_SDHCI_CNS3XXX
If unsure, say N. If unsure, say N.
config MMC_SDHCI_ESDHC_IMX config MMC_SDHCI_ESDHC_IMX
tristate "SDHCI platform support for the Freescale eSDHC i.MX controller" tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
depends on ARCH_MX25 || ARCH_MX35 || ARCH_MX5 depends on ARCH_MXC
depends on MMC_SDHCI_PLTFM depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS select MMC_SDHCI_IO_ACCESSORS
help help
This selects the Freescale eSDHC controller support on the platform This selects the Freescale eSDHC/uSDHC controller support
bus, found on platforms like mx35/51. found on i.MX25, i.MX35 i.MX5x and i.MX6x.
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.
@ -326,11 +326,11 @@ config MMC_MSM
support for SDIO devices. support for SDIO devices.
config MMC_MXC config MMC_MXC
tristate "Freescale i.MX2/3 Multimedia Card Interface support" tristate "Freescale i.MX21/27/31 Multimedia Card Interface support"
depends on MACH_MX21 || MACH_MX27 || ARCH_MX31 depends on ARCH_MXC
help help
This selects the Freescale i.MX2/3 Multimedia card Interface. This selects the Freescale i.MX21, i.MX27 and i.MX31 Multimedia card
If you have a i.MX platform with a Multimedia Card slot, Interface. If you have a i.MX platform with a Multimedia Card slot,
say Y or M here. say Y or M here.
If unsure, say N. If unsure, say N.

View File

@ -869,7 +869,11 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
static irqreturn_t at91_mmc_det_irq(int irq, void *_host) static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
{ {
struct at91mci_host *host = _host; struct at91mci_host *host = _host;
int present = !gpio_get_value(irq_to_gpio(irq)); int present;
/* entering this ISR means that we have configured det_pin:
* we can use its value in board structure */
present = !gpio_get_value(host->board->det_pin);
/* /*
* we expect this irq on both insert and remove, * we expect this irq on both insert and remove,

View File

@ -17,112 +17,126 @@
#define __DRIVERS_MMC_ATMEL_MCI_H__ #define __DRIVERS_MMC_ATMEL_MCI_H__
/* MCI Register Definitions */ /* MCI Register Definitions */
#define MCI_CR 0x0000 /* Control */ #define ATMCI_CR 0x0000 /* Control */
# define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */ # define ATMCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
# define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */ # define ATMCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
# define MCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */ # define ATMCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
# define MCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */ # define ATMCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
# define MCI_CR_SWRST ( 1 << 7) /* Software Reset */ # define ATMCI_CR_SWRST ( 1 << 7) /* Software Reset */
#define MCI_MR 0x0004 /* Mode */ #define ATMCI_MR 0x0004 /* Mode */
# define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */ # define ATMCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
# define MCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */ # define ATMCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
# define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */ # define ATMCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
# define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */ # define ATMCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
# define MCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */ # define ATMCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
# define MCI_MR_PDCPADV ( 1 << 14) /* Padding Value */ # define ATMCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
# define MCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */ # define ATMCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
#define MCI_DTOR 0x0008 /* Data Timeout */ #define ATMCI_DTOR 0x0008 /* Data Timeout */
# define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */ # define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
# define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */ # define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
#define MCI_SDCR 0x000c /* SD Card / SDIO */ #define ATMCI_SDCR 0x000c /* SD Card / SDIO */
# define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */ # define ATMCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
# define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */ # define ATMCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
# define MCI_SDCSEL_MASK ( 3 << 0) # define ATMCI_SDCSEL_MASK ( 3 << 0)
# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */ # define ATMCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */ # define ATMCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
# define MCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */ # define ATMCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
# define MCI_SDCBUS_MASK ( 3 << 6) # define ATMCI_SDCBUS_MASK ( 3 << 6)
#define MCI_ARGR 0x0010 /* Command Argument */ #define ATMCI_ARGR 0x0010 /* Command Argument */
#define MCI_CMDR 0x0014 /* Command */ #define ATMCI_CMDR 0x0014 /* Command */
# define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */ # define ATMCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
# define MCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */ # define ATMCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
# define MCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */ # define ATMCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
# define MCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */ # define ATMCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
# define MCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */ # define ATMCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
# define MCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */ # define ATMCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
# define MCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */ # define ATMCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
# define MCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */ # define ATMCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
# define MCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */ # define ATMCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
# define MCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */ # define ATMCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
# define MCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */ # define ATMCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
# define MCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */ # define ATMCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
# define MCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */ # define ATMCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
# define MCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */ # define ATMCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
# define MCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */ # define ATMCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
# define MCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */ # define ATMCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
# define MCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */ # define ATMCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
# define MCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */ # define ATMCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
# define MCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */ # define ATMCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
# define MCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */ # define ATMCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
# define MCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */ # define ATMCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
# define MCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */ # define ATMCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
#define MCI_BLKR 0x0018 /* Block */ #define ATMCI_BLKR 0x0018 /* Block */
# define MCI_BCNT(x) ((x) << 0) /* Data Block Count */ # define ATMCI_BCNT(x) ((x) << 0) /* Data Block Count */
# define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */ # define ATMCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
#define MCI_CSTOR 0x001c /* Completion Signal Timeout[2] */ #define ATMCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
# define MCI_CSTOCYC(x) ((x) << 0) /* CST cycles */ # define ATMCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
# define MCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */ # define ATMCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
#define MCI_RSPR 0x0020 /* Response 0 */ #define ATMCI_RSPR 0x0020 /* Response 0 */
#define MCI_RSPR1 0x0024 /* Response 1 */ #define ATMCI_RSPR1 0x0024 /* Response 1 */
#define MCI_RSPR2 0x0028 /* Response 2 */ #define ATMCI_RSPR2 0x0028 /* Response 2 */
#define MCI_RSPR3 0x002c /* Response 3 */ #define ATMCI_RSPR3 0x002c /* Response 3 */
#define MCI_RDR 0x0030 /* Receive Data */ #define ATMCI_RDR 0x0030 /* Receive Data */
#define MCI_TDR 0x0034 /* Transmit Data */ #define ATMCI_TDR 0x0034 /* Transmit Data */
#define MCI_SR 0x0040 /* Status */ #define ATMCI_SR 0x0040 /* Status */
#define MCI_IER 0x0044 /* Interrupt Enable */ #define ATMCI_IER 0x0044 /* Interrupt Enable */
#define MCI_IDR 0x0048 /* Interrupt Disable */ #define ATMCI_IDR 0x0048 /* Interrupt Disable */
#define MCI_IMR 0x004c /* Interrupt Mask */ #define ATMCI_IMR 0x004c /* Interrupt Mask */
# define MCI_CMDRDY ( 1 << 0) /* Command Ready */ # define ATMCI_CMDRDY ( 1 << 0) /* Command Ready */
# define MCI_RXRDY ( 1 << 1) /* Receiver Ready */ # define ATMCI_RXRDY ( 1 << 1) /* Receiver Ready */
# define MCI_TXRDY ( 1 << 2) /* Transmitter Ready */ # define ATMCI_TXRDY ( 1 << 2) /* Transmitter Ready */
# define MCI_BLKE ( 1 << 3) /* Data Block Ended */ # define ATMCI_BLKE ( 1 << 3) /* Data Block Ended */
# define MCI_DTIP ( 1 << 4) /* Data Transfer In Progress */ # define ATMCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
# define MCI_NOTBUSY ( 1 << 5) /* Data Not Busy */ # define ATMCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
# define MCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */ # define ATMCI_ENDRX ( 1 << 6) /* End of RX Buffer */
# define MCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */ # define ATMCI_ENDTX ( 1 << 7) /* End of TX Buffer */
# define MCI_RINDE ( 1 << 16) /* Response Index Error */ # define ATMCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
# define MCI_RDIRE ( 1 << 17) /* Response Direction Error */ # define ATMCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
# define MCI_RCRCE ( 1 << 18) /* Response CRC Error */ # define ATMCI_SDIOWAIT ( 1 << 12) /* SDIO Read Wait Operation Status */
# define MCI_RENDE ( 1 << 19) /* Response End Bit Error */ # define ATMCI_CSRCV ( 1 << 13) /* CE-ATA Completion Signal Received */
# define MCI_RTOE ( 1 << 20) /* Response Time-Out Error */ # define ATMCI_RXBUFF ( 1 << 14) /* RX Buffer Full */
# define MCI_DCRCE ( 1 << 21) /* Data CRC Error */ # define ATMCI_TXBUFE ( 1 << 15) /* TX Buffer Empty */
# define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */ # define ATMCI_RINDE ( 1 << 16) /* Response Index Error */
# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */ # define ATMCI_RDIRE ( 1 << 17) /* Response Direction Error */
# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */ # define ATMCI_RCRCE ( 1 << 18) /* Response CRC Error */
#define MCI_DMA 0x0050 /* DMA Configuration[2] */ # define ATMCI_RENDE ( 1 << 19) /* Response End Bit Error */
# define MCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */ # define ATMCI_RTOE ( 1 << 20) /* Response Time-Out Error */
# define MCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */ # define ATMCI_DCRCE ( 1 << 21) /* Data CRC Error */
# define MCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */ # define ATMCI_DTOE ( 1 << 22) /* Data Time-Out Error */
#define MCI_CFG 0x0054 /* Configuration[2] */ # define ATMCI_CSTOE ( 1 << 23) /* Completion Signal Time-out Error */
# define MCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */ # define ATMCI_BLKOVRE ( 1 << 24) /* DMA Block Overrun Error */
# define MCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */ # define ATMCI_DMADONE ( 1 << 25) /* DMA Transfer Done */
# define MCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */ # define ATMCI_FIFOEMPTY ( 1 << 26) /* FIFO Empty Flag */
# define MCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */ # define ATMCI_XFRDONE ( 1 << 27) /* Transfer Done Flag */
#define MCI_WPMR 0x00e4 /* Write Protection Mode[2] */ # define ATMCI_ACKRCV ( 1 << 28) /* Boot Operation Acknowledge Received */
# define MCI_WP_EN ( 1 << 0) /* WP Enable */ # define ATMCI_ACKRCVE ( 1 << 29) /* Boot Operation Acknowledge Error */
# define MCI_WP_KEY (0x4d4349 << 8) /* WP Key */ # define ATMCI_OVRE ( 1 << 30) /* RX Overrun Error */
#define MCI_WPSR 0x00e8 /* Write Protection Status[2] */ # define ATMCI_UNRE ( 1 << 31) /* TX Underrun Error */
# define MCI_GET_WP_VS(x) ((x) & 0x0f) #define ATMCI_DMA 0x0050 /* DMA Configuration[2] */
# define MCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff) # define ATMCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
#define MCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */ # define ATMCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
# define ATMCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
#define ATMCI_CFG 0x0054 /* Configuration[2] */
# define ATMCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
# define ATMCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
# define ATMCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
# define ATMCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
#define ATMCI_WPMR 0x00e4 /* Write Protection Mode[2] */
# define ATMCI_WP_EN ( 1 << 0) /* WP Enable */
# define ATMCI_WP_KEY (0x4d4349 << 8) /* WP Key */
#define ATMCI_WPSR 0x00e8 /* Write Protection Status[2] */
# define ATMCI_GET_WP_VS(x) ((x) & 0x0f)
# define ATMCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
#define ATMCI_VERSION 0x00FC /* Version */
#define ATMCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
/* This is not including the FIFO Aperture on MCI2 */ /* This is not including the FIFO Aperture on MCI2 */
#define MCI_REGS_SIZE 0x100 #define ATMCI_REGS_SIZE 0x100
/* Register access macros */ /* Register access macros */
#define mci_readl(port,reg) \ #define atmci_readl(port,reg) \
__raw_readl((port)->regs + MCI_##reg) __raw_readl((port)->regs + reg)
#define mci_writel(port,reg,value) \ #define atmci_writel(port,reg,value) \
__raw_writel((value), (port)->regs + MCI_##reg) __raw_writel((value), (port)->regs + reg)
#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */ #endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,7 @@
#ifdef DEBUG #ifdef DEBUG
#define DBG(fmt, idx, args...) \ #define DBG(fmt, idx, args...) \
printk(KERN_DEBUG "au1xmmc(%d): DEBUG: " fmt, idx, ##args) pr_debug("au1xmmc(%d): DEBUG: " fmt, idx, ##args)
#else #else
#define DBG(fmt, idx, args...) do {} while (0) #define DBG(fmt, idx, args...) do {} while (0)
#endif #endif
@ -268,7 +268,7 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
mmccmd |= SD_CMD_RT_3; mmccmd |= SD_CMD_RT_3;
break; break;
default: default:
printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", pr_info("au1xmmc: unhandled response type %02x\n",
mmc_resp_type(cmd)); mmc_resp_type(cmd));
return -EINVAL; return -EINVAL;
} }
@ -1031,7 +1031,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
#ifdef CONFIG_SOC_AU1200 #ifdef CONFIG_SOC_AU1200
ret = au1xmmc_dbdma_init(host); ret = au1xmmc_dbdma_init(host);
if (ret) if (ret)
printk(KERN_INFO DRIVER_NAME ": DBDMA init failed; using PIO\n"); pr_info(DRIVER_NAME ": DBDMA init failed; using PIO\n");
#endif #endif
#ifdef CONFIG_LEDS_CLASS #ifdef CONFIG_LEDS_CLASS
@ -1056,7 +1056,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X" pr_info(DRIVER_NAME ": MMC Controller %d set up at %8.8X"
" (mode=%s)\n", pdev->id, host->iobase, " (mode=%s)\n", pdev->id, host->iobase,
host->flags & HOST_F_DMA ? "dma" : "pio"); host->flags & HOST_F_DMA ? "dma" : "pio");
@ -1188,7 +1188,7 @@ static int __init au1xmmc_init(void)
*/ */
memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
if (!memid) if (!memid)
printk(KERN_ERR "au1xmmc: cannot add memory dbdma dev\n"); pr_err("au1xmmc: cannot add memory dbdma dev\n");
#endif #endif
return platform_driver_register(&au1xmmc_driver); return platform_driver_register(&au1xmmc_driver);
} }

View File

@ -764,11 +764,29 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
return present; return present;
} }
static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
u32 int_mask;
/* Enable/disable Slot Specific SDIO interrupt */
int_mask = mci_readl(host, INTMASK);
if (enb) {
mci_writel(host, INTMASK,
(int_mask | (1 << SDMMC_INT_SDIO(slot->id))));
} else {
mci_writel(host, INTMASK,
(int_mask & ~(1 << SDMMC_INT_SDIO(slot->id))));
}
}
static const struct mmc_host_ops dw_mci_ops = { static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request, .request = dw_mci_request,
.set_ios = dw_mci_set_ios, .set_ios = dw_mci_set_ios,
.get_ro = dw_mci_get_ro, .get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd, .get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq,
}; };
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@ -1025,7 +1043,8 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
buf += len; buf += len;
cnt -= len; cnt -= len;
if (!sg_next(host->sg) || host->part_buf_count == 2) { if (!sg_next(host->sg) || host->part_buf_count == 2) {
mci_writew(host, DATA, host->part_buf16); mci_writew(host, DATA(host->data_offset),
host->part_buf16);
host->part_buf_count = 0; host->part_buf_count = 0;
} }
} }
@ -1042,21 +1061,23 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
cnt -= len; cnt -= len;
/* push data from aligned buffer into fifo */ /* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i) for (i = 0; i < items; ++i)
mci_writew(host, DATA, aligned_buf[i]); mci_writew(host, DATA(host->data_offset),
aligned_buf[i]);
} }
} else } else
#endif #endif
{ {
u16 *pdata = buf; u16 *pdata = buf;
for (; cnt >= 2; cnt -= 2) for (; cnt >= 2; cnt -= 2)
mci_writew(host, DATA, *pdata++); mci_writew(host, DATA(host->data_offset), *pdata++);
buf = pdata; buf = pdata;
} }
/* put anything remaining in the part_buf */ /* put anything remaining in the part_buf */
if (cnt) { if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt); dw_mci_set_part_bytes(host, buf, cnt);
if (!sg_next(host->sg)) if (!sg_next(host->sg))
mci_writew(host, DATA, host->part_buf16); mci_writew(host, DATA(host->data_offset),
host->part_buf16);
} }
} }
@ -1071,7 +1092,8 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
int items = len >> 1; int items = len >> 1;
int i; int i;
for (i = 0; i < items; ++i) for (i = 0; i < items; ++i)
aligned_buf[i] = mci_readw(host, DATA); aligned_buf[i] = mci_readw(host,
DATA(host->data_offset));
/* memcpy from aligned buffer into output buffer */ /* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len); memcpy(buf, aligned_buf, len);
buf += len; buf += len;
@ -1082,11 +1104,11 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
{ {
u16 *pdata = buf; u16 *pdata = buf;
for (; cnt >= 2; cnt -= 2) for (; cnt >= 2; cnt -= 2)
*pdata++ = mci_readw(host, DATA); *pdata++ = mci_readw(host, DATA(host->data_offset));
buf = pdata; buf = pdata;
} }
if (cnt) { if (cnt) {
host->part_buf16 = mci_readw(host, DATA); host->part_buf16 = mci_readw(host, DATA(host->data_offset));
dw_mci_pull_final_bytes(host, buf, cnt); dw_mci_pull_final_bytes(host, buf, cnt);
} }
} }
@ -1099,7 +1121,8 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
buf += len; buf += len;
cnt -= len; cnt -= len;
if (!sg_next(host->sg) || host->part_buf_count == 4) { if (!sg_next(host->sg) || host->part_buf_count == 4) {
mci_writel(host, DATA, host->part_buf32); mci_writel(host, DATA(host->data_offset),
host->part_buf32);
host->part_buf_count = 0; host->part_buf_count = 0;
} }
} }
@ -1116,21 +1139,23 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
cnt -= len; cnt -= len;
/* push data from aligned buffer into fifo */ /* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i) for (i = 0; i < items; ++i)
mci_writel(host, DATA, aligned_buf[i]); mci_writel(host, DATA(host->data_offset),
aligned_buf[i]);
} }
} else } else
#endif #endif
{ {
u32 *pdata = buf; u32 *pdata = buf;
for (; cnt >= 4; cnt -= 4) for (; cnt >= 4; cnt -= 4)
mci_writel(host, DATA, *pdata++); mci_writel(host, DATA(host->data_offset), *pdata++);
buf = pdata; buf = pdata;
} }
/* put anything remaining in the part_buf */ /* put anything remaining in the part_buf */
if (cnt) { if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt); dw_mci_set_part_bytes(host, buf, cnt);
if (!sg_next(host->sg)) if (!sg_next(host->sg))
mci_writel(host, DATA, host->part_buf32); mci_writel(host, DATA(host->data_offset),
host->part_buf32);
} }
} }
@ -1145,7 +1170,8 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
int items = len >> 2; int items = len >> 2;
int i; int i;
for (i = 0; i < items; ++i) for (i = 0; i < items; ++i)
aligned_buf[i] = mci_readl(host, DATA); aligned_buf[i] = mci_readl(host,
DATA(host->data_offset));
/* memcpy from aligned buffer into output buffer */ /* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len); memcpy(buf, aligned_buf, len);
buf += len; buf += len;
@ -1156,11 +1182,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
{ {
u32 *pdata = buf; u32 *pdata = buf;
for (; cnt >= 4; cnt -= 4) for (; cnt >= 4; cnt -= 4)
*pdata++ = mci_readl(host, DATA); *pdata++ = mci_readl(host, DATA(host->data_offset));
buf = pdata; buf = pdata;
} }
if (cnt) { if (cnt) {
host->part_buf32 = mci_readl(host, DATA); host->part_buf32 = mci_readl(host, DATA(host->data_offset));
dw_mci_pull_final_bytes(host, buf, cnt); dw_mci_pull_final_bytes(host, buf, cnt);
} }
} }
@ -1173,7 +1199,8 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
buf += len; buf += len;
cnt -= len; cnt -= len;
if (!sg_next(host->sg) || host->part_buf_count == 8) { if (!sg_next(host->sg) || host->part_buf_count == 8) {
mci_writew(host, DATA, host->part_buf); mci_writew(host, DATA(host->data_offset),
host->part_buf);
host->part_buf_count = 0; host->part_buf_count = 0;
} }
} }
@ -1190,21 +1217,23 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
cnt -= len; cnt -= len;
/* push data from aligned buffer into fifo */ /* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i) for (i = 0; i < items; ++i)
mci_writeq(host, DATA, aligned_buf[i]); mci_writeq(host, DATA(host->data_offset),
aligned_buf[i]);
} }
} else } else
#endif #endif
{ {
u64 *pdata = buf; u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8) for (; cnt >= 8; cnt -= 8)
mci_writeq(host, DATA, *pdata++); mci_writeq(host, DATA(host->data_offset), *pdata++);
buf = pdata; buf = pdata;
} }
/* put anything remaining in the part_buf */ /* put anything remaining in the part_buf */
if (cnt) { if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt); dw_mci_set_part_bytes(host, buf, cnt);
if (!sg_next(host->sg)) if (!sg_next(host->sg))
mci_writeq(host, DATA, host->part_buf); mci_writeq(host, DATA(host->data_offset),
host->part_buf);
} }
} }
@ -1219,7 +1248,8 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
int items = len >> 3; int items = len >> 3;
int i; int i;
for (i = 0; i < items; ++i) for (i = 0; i < items; ++i)
aligned_buf[i] = mci_readq(host, DATA); aligned_buf[i] = mci_readq(host,
DATA(host->data_offset));
/* memcpy from aligned buffer into output buffer */ /* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len); memcpy(buf, aligned_buf, len);
buf += len; buf += len;
@ -1230,11 +1260,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
{ {
u64 *pdata = buf; u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8) for (; cnt >= 8; cnt -= 8)
*pdata++ = mci_readq(host, DATA); *pdata++ = mci_readq(host, DATA(host->data_offset));
buf = pdata; buf = pdata;
} }
if (cnt) { if (cnt) {
host->part_buf = mci_readq(host, DATA); host->part_buf = mci_readq(host, DATA(host->data_offset));
dw_mci_pull_final_bytes(host, buf, cnt); dw_mci_pull_final_bytes(host, buf, cnt);
} }
} }
@ -1406,6 +1436,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
struct dw_mci *host = dev_id; struct dw_mci *host = dev_id;
u32 status, pending; u32 status, pending;
unsigned int pass_count = 0; unsigned int pass_count = 0;
int i;
do { do {
status = mci_readl(host, RINTSTS); status = mci_readl(host, RINTSTS);
@ -1477,6 +1508,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
queue_work(dw_mci_card_workqueue, &host->card_work); queue_work(dw_mci_card_workqueue, &host->card_work);
} }
/* Handle SDIO Interrupts */
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (pending & SDMMC_INT_SDIO(i)) {
mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
mmc_signal_sdio_irq(slot->mmc);
}
}
} while (pass_count++ < 5); } while (pass_count++ < 5);
#ifdef CONFIG_MMC_DW_IDMAC #ifdef CONFIG_MMC_DW_IDMAC
@ -1673,7 +1713,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) { if (IS_ERR(host->vmmc)) {
printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc)); pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL; host->vmmc = NULL;
} else } else
regulator_enable(host->vmmc); regulator_enable(host->vmmc);
@ -1923,6 +1963,18 @@ static int dw_mci_probe(struct platform_device *pdev)
} }
} }
/*
* In 2.40a spec, Data offset is changed.
* Need to check the version-id and set data-offset for DATA register.
*/
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
dev_info(&pdev->dev, "Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A)
host->data_offset = DATA_OFFSET;
else
host->data_offset = DATA_240A_OFFSET;
/* /*
* Enable interrupts for command done, data over, data empty, card det, * Enable interrupts for command done, data over, data empty, card det,
* receive ready and error such as transmit, receive timeout, crc error * receive ready and error such as transmit, receive timeout, crc error

View File

@ -14,6 +14,8 @@
#ifndef _DW_MMC_H_ #ifndef _DW_MMC_H_
#define _DW_MMC_H_ #define _DW_MMC_H_
#define DW_MMC_240A 0x240a
#define SDMMC_CTRL 0x000 #define SDMMC_CTRL 0x000
#define SDMMC_PWREN 0x004 #define SDMMC_PWREN 0x004
#define SDMMC_CLKDIV 0x008 #define SDMMC_CLKDIV 0x008
@ -51,7 +53,14 @@
#define SDMMC_IDINTEN 0x090 #define SDMMC_IDINTEN 0x090
#define SDMMC_DSCADDR 0x094 #define SDMMC_DSCADDR 0x094
#define SDMMC_BUFADDR 0x098 #define SDMMC_BUFADDR 0x098
#define SDMMC_DATA 0x100 #define SDMMC_DATA(x) (x)
/*
* Data offset is difference according to Version
* Lower than 2.40a : data register offest is 0x100
*/
#define DATA_OFFSET 0x100
#define DATA_240A_OFFSET 0x200
/* shift bit field */ /* shift bit field */
#define _SBF(f, v) ((v) << (f)) #define _SBF(f, v) ((v) << (f))
@ -82,7 +91,7 @@
#define SDMMC_CTYPE_4BIT BIT(0) #define SDMMC_CTYPE_4BIT BIT(0)
#define SDMMC_CTYPE_1BIT 0 #define SDMMC_CTYPE_1BIT 0
/* Interrupt status & mask register defines */ /* Interrupt status & mask register defines */
#define SDMMC_INT_SDIO BIT(16) #define SDMMC_INT_SDIO(n) BIT(16 + (n))
#define SDMMC_INT_EBE BIT(15) #define SDMMC_INT_EBE BIT(15)
#define SDMMC_INT_ACD BIT(14) #define SDMMC_INT_ACD BIT(14)
#define SDMMC_INT_SBE BIT(13) #define SDMMC_INT_SBE BIT(13)
@ -130,6 +139,8 @@
#define SDMMC_IDMAC_ENABLE BIT(7) #define SDMMC_IDMAC_ENABLE BIT(7)
#define SDMMC_IDMAC_FB BIT(1) #define SDMMC_IDMAC_FB BIT(1)
#define SDMMC_IDMAC_SWRESET BIT(0) #define SDMMC_IDMAC_SWRESET BIT(0)
/* Version ID register define */
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Register access macros */ /* Register access macros */
#define mci_readl(dev, reg) \ #define mci_readl(dev, reg) \

View File

@ -942,7 +942,7 @@ static int __init imxmci_probe(struct platform_device *pdev)
int ret = 0, irq; int ret = 0, irq;
u16 rev_no; u16 rev_no;
printk(KERN_INFO "i.MX mmc driver\n"); pr_info("i.MX mmc driver\n");
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);

View File

@ -27,6 +27,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h>
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/crc7.h> #include <linux/crc7.h>

View File

@ -466,7 +466,7 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
struct mmci_host_next *next = &host->next_data; struct mmci_host_next *next = &host->next_data;
if (data->host_cookie && data->host_cookie != next->cookie) { if (data->host_cookie && data->host_cookie != next->cookie) {
printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d" pr_warning("[%s] invalid cookie: data->host_cookie %d"
" host->next_data.cookie %d\n", " host->next_data.cookie %d\n",
__func__, data->host_cookie, host->next_data.cookie); __func__, data->host_cookie, host->next_data.cookie);
data->host_cookie = 0; data->host_cookie = 0;
@ -531,7 +531,7 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
if (chan) { if (chan) {
if (err) if (err)
dmaengine_terminate_all(chan); dmaengine_terminate_all(chan);
if (err || data->host_cookie) if (data->host_cookie)
dma_unmap_sg(mmc_dev(host->mmc), data->sg, dma_unmap_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, dir); data->sg_len, dir);
mrq->data->host_cookie = 0; mrq->data->host_cookie = 0;

View File

@ -213,7 +213,8 @@ msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER); msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
msmsdcc_writel(host, (unsigned int)host->curr.xfer_size, msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
MMCIDATALENGTH); MMCIDATALENGTH);
msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1); msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
(~MCI_IRQ_PIO)) | host->cmd_pio_irqmask, MMCIMASK0);
msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL); msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);
if (host->cmd_cmd) { if (host->cmd_cmd) {
@ -388,7 +389,7 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg, n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
host->dma.num_ents, host->dma.dir); host->dma.num_ents, host->dma.dir);
if (n == 0) { if (n == 0) {
printk(KERN_ERR "%s: Unable to map in all sg elements\n", pr_err("%s: Unable to map in all sg elements\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
host->dma.sg = NULL; host->dma.sg = NULL;
host->dma.num_ents = 0; host->dma.num_ents = 0;
@ -474,7 +475,7 @@ msmsdcc_start_command_deferred(struct msmsdcc_host *host,
*c |= MCI_CSPM_MCIABORT; *c |= MCI_CSPM_MCIABORT;
if (host->curr.cmd != NULL) { if (host->curr.cmd != NULL) {
printk(KERN_ERR "%s: Overlapping command requests\n", pr_err("%s: Overlapping command requests\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
} }
host->curr.cmd = cmd; host->curr.cmd = cmd;
@ -543,7 +544,9 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH); msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
msmsdcc_writel(host, pio_irqmask, MMCIMASK1); msmsdcc_writel(host, (msmsdcc_readl(host, MMCIMASK0) &
(~MCI_IRQ_PIO)) | pio_irqmask, MMCIMASK0);
msmsdcc_writel(host, datactrl, MMCIDATACTRL); msmsdcc_writel(host, datactrl, MMCIDATACTRL);
if (cmd) { if (cmd) {
@ -659,8 +662,13 @@ msmsdcc_pio_irq(int irq, void *dev_id)
{ {
struct msmsdcc_host *host = dev_id; struct msmsdcc_host *host = dev_id;
uint32_t status; uint32_t status;
u32 mci_mask0;
status = msmsdcc_readl(host, MMCISTATUS); status = msmsdcc_readl(host, MMCISTATUS);
mci_mask0 = msmsdcc_readl(host, MMCIMASK0);
if (((mci_mask0 & status) & MCI_IRQ_PIO) == 0)
return IRQ_NONE;
do { do {
unsigned long flags; unsigned long flags;
@ -719,10 +727,12 @@ msmsdcc_pio_irq(int irq, void *dev_id)
} while (1); } while (1);
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE) if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1); msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) |
MCI_RXDATAAVLBLMASK, MMCIMASK0);
if (!host->curr.xfer_remain) if (!host->curr.xfer_remain)
msmsdcc_writel(host, 0, MMCIMASK1); msmsdcc_writel(host, (mci_mask0 & (~MCI_IRQ_PIO)) | 0,
MMCIMASK0);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -854,6 +864,8 @@ msmsdcc_irq(int irq, void *dev_id)
do { do {
status = msmsdcc_readl(host, MMCISTATUS); status = msmsdcc_readl(host, MMCISTATUS);
status &= msmsdcc_readl(host, MMCIMASK0); status &= msmsdcc_readl(host, MMCIMASK0);
if ((status & (~MCI_IRQ_PIO)) == 0)
break;
msmsdcc_writel(host, status, MMCICLEAR); msmsdcc_writel(host, status, MMCICLEAR);
if (status & MCI_SDIOINTR) if (status & MCI_SDIOINTR)
@ -939,7 +951,7 @@ static void msmsdcc_setup_gpio(struct msmsdcc_host *host, bool enable)
struct msm_mmc_gpio_data *curr; struct msm_mmc_gpio_data *curr;
int i, rc = 0; int i, rc = 0;
if (!host->plat->gpio_data && host->gpio_config_status == enable) if (!host->plat->gpio_data || host->gpio_config_status == enable)
return; return;
curr = host->plat->gpio_data; curr = host->plat->gpio_data;
@ -1052,10 +1064,19 @@ static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static void msmsdcc_init_card(struct mmc_host *mmc, struct mmc_card *card)
{
struct msmsdcc_host *host = mmc_priv(mmc);
if (host->plat->init_card)
host->plat->init_card(card);
}
static const struct mmc_host_ops msmsdcc_ops = { static const struct mmc_host_ops msmsdcc_ops = {
.request = msmsdcc_request, .request = msmsdcc_request,
.set_ios = msmsdcc_set_ios, .set_ios = msmsdcc_set_ios,
.enable_sdio_irq = msmsdcc_enable_sdio_irq, .enable_sdio_irq = msmsdcc_enable_sdio_irq,
.init_card = msmsdcc_init_card,
}; };
static void static void
@ -1092,7 +1113,7 @@ msmsdcc_platform_status_irq(int irq, void *dev_id)
{ {
struct msmsdcc_host *host = dev_id; struct msmsdcc_host *host = dev_id;
printk(KERN_DEBUG "%s: %d\n", __func__, irq); pr_debug("%s: %d\n", __func__, irq);
msmsdcc_check_status((unsigned long) host); msmsdcc_check_status((unsigned long) host);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -1102,7 +1123,7 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id)
{ {
struct msmsdcc_host *host = dev_id; struct msmsdcc_host *host = dev_id;
printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc), pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc),
card_present); card_present);
msmsdcc_check_status((unsigned long) host); msmsdcc_check_status((unsigned long) host);
} }
@ -1150,7 +1171,6 @@ msmsdcc_probe(struct platform_device *pdev)
struct msmsdcc_host *host; struct msmsdcc_host *host;
struct mmc_host *mmc; struct mmc_host *mmc;
struct resource *cmd_irqres = NULL; struct resource *cmd_irqres = NULL;
struct resource *pio_irqres = NULL;
struct resource *stat_irqres = NULL; struct resource *stat_irqres = NULL;
struct resource *memres = NULL; struct resource *memres = NULL;
struct resource *dmares = NULL; struct resource *dmares = NULL;
@ -1175,12 +1195,10 @@ msmsdcc_probe(struct platform_device *pdev)
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"cmd_irq"); "cmd_irq");
pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"pio_irq");
stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"status_irq"); "status_irq");
if (!cmd_irqres || !pio_irqres || !memres) { if (!cmd_irqres || !memres) {
pr_err("%s: Invalid resource\n", __func__); pr_err("%s: Invalid resource\n", __func__);
return -ENXIO; return -ENXIO;
} }
@ -1200,17 +1218,20 @@ msmsdcc_probe(struct platform_device *pdev)
host->plat = plat; host->plat = plat;
host->mmc = mmc; host->mmc = mmc;
host->curr.cmd = NULL; host->curr.cmd = NULL;
init_timer(&host->busclk_timer);
host->busclk_timer.data = (unsigned long) host;
host->busclk_timer.function = msmsdcc_busclk_expired;
host->cmdpoll = 1; host->cmdpoll = 1;
host->base = ioremap(memres->start, PAGE_SIZE); host->base = ioremap(memres->start, PAGE_SIZE);
if (!host->base) { if (!host->base) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto host_free;
} }
host->cmd_irqres = cmd_irqres; host->cmd_irqres = cmd_irqres;
host->pio_irqres = pio_irqres;
host->memres = memres; host->memres = memres;
host->dmares = dmares; host->dmares = dmares;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
@ -1221,13 +1242,19 @@ msmsdcc_probe(struct platform_device *pdev)
/* /*
* Setup DMA * Setup DMA
*/ */
msmsdcc_init_dma(host); if (host->dmares) {
ret = msmsdcc_init_dma(host);
if (ret)
goto ioremap_free;
} else {
host->dma.channel = -1;
}
/* Get our clocks */ /* Get our clocks */
host->pclk = clk_get(&pdev->dev, "sdc_pclk"); host->pclk = clk_get(&pdev->dev, "sdc_pclk");
if (IS_ERR(host->pclk)) { if (IS_ERR(host->pclk)) {
ret = PTR_ERR(host->pclk); ret = PTR_ERR(host->pclk);
goto host_free; goto dma_free;
} }
host->clk = clk_get(&pdev->dev, "sdc_clk"); host->clk = clk_get(&pdev->dev, "sdc_clk");
@ -1236,17 +1263,17 @@ msmsdcc_probe(struct platform_device *pdev)
goto pclk_put; goto pclk_put;
} }
ret = clk_set_rate(host->clk, msmsdcc_fmin);
if (ret) {
pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
goto clk_put;
}
/* Enable clocks */ /* Enable clocks */
ret = msmsdcc_enable_clocks(host); ret = msmsdcc_enable_clocks(host);
if (ret) if (ret)
goto clk_put; goto clk_put;
ret = clk_set_rate(host->clk, msmsdcc_fmin);
if (ret) {
pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
goto clk_disable;
}
host->pclk_rate = clk_get_rate(host->pclk); host->pclk_rate = clk_get_rate(host->pclk);
host->clk_rate = clk_get_rate(host->clk); host->clk_rate = clk_get_rate(host->clk);
@ -1316,16 +1343,12 @@ msmsdcc_probe(struct platform_device *pdev)
host->eject = !host->oldstat; host->eject = !host->oldstat;
} }
init_timer(&host->busclk_timer);
host->busclk_timer.data = (unsigned long) host;
host->busclk_timer.function = msmsdcc_busclk_expired;
ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED, ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
DRIVER_NAME " (cmd)", host); DRIVER_NAME " (cmd)", host);
if (ret) if (ret)
goto stat_irq_free; goto stat_irq_free;
ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED, ret = request_irq(cmd_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
DRIVER_NAME " (pio)", host); DRIVER_NAME " (pio)", host);
if (ret) if (ret)
goto cmd_irq_free; goto cmd_irq_free;
@ -1368,6 +1391,13 @@ msmsdcc_probe(struct platform_device *pdev)
clk_put(host->clk); clk_put(host->clk);
pclk_put: pclk_put:
clk_put(host->pclk); clk_put(host->pclk);
dma_free:
if (host->dmares)
dma_free_coherent(NULL, sizeof(struct msmsdcc_nc_dmadata),
host->dma.nc, host->dma.nc_busaddr);
ioremap_free:
tasklet_kill(&host->dma_tlet);
iounmap(host->base);
host_free: host_free:
mmc_free_host(mmc); mmc_free_host(mmc);
out: out:

View File

@ -140,6 +140,11 @@
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK) MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|MCI_PROGDONEMASK)
#define MCI_IRQ_PIO \
(MCI_RXDATAAVLBLMASK | MCI_TXDATAAVLBLMASK | MCI_RXFIFOEMPTYMASK | \
MCI_TXFIFOEMPTYMASK | MCI_RXFIFOFULLMASK | MCI_TXFIFOFULLMASK | \
MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK | \
MCI_RXACTIVEMASK | MCI_TXACTIVEMASK)
/* /*
* The size of the FIFO in bytes. * The size of the FIFO in bytes.
*/ */
@ -202,7 +207,6 @@ struct msmsdcc_stats {
struct msmsdcc_host { struct msmsdcc_host {
struct resource *cmd_irqres; struct resource *cmd_irqres;
struct resource *pio_irqres;
struct resource *memres; struct resource *memres;
struct resource *dmares; struct resource *dmares;
void __iomem *base; void __iomem *base;

View File

@ -117,7 +117,7 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
host->pio_size = data->blocks * data->blksz; host->pio_size = data->blocks * data->blksz;
host->pio_ptr = sg_virt(data->sg); host->pio_ptr = sg_virt(data->sg);
if (!nodma) if (!nodma)
printk(KERN_DEBUG "%s: fallback to PIO for data " pr_debug("%s: fallback to PIO for data "
"at 0x%p size %d\n", "at 0x%p size %d\n",
mmc_hostname(host->mmc), mmc_hostname(host->mmc),
host->pio_ptr, host->pio_size); host->pio_ptr, host->pio_size);
@ -471,7 +471,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
if (mrq->data) if (mrq->data)
err_status = mvsd_finish_data(host, mrq->data, err_status); err_status = mvsd_finish_data(host, mrq->data, err_status);
if (err_status) { if (err_status) {
printk(KERN_ERR "%s: unhandled error status %#04x\n", pr_err("%s: unhandled error status %#04x\n",
mmc_hostname(host->mmc), err_status); mmc_hostname(host->mmc), err_status);
cmd->error = -ENOMSG; cmd->error = -ENOMSG;
} }
@ -489,7 +489,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
if (irq_handled) if (irq_handled)
return IRQ_HANDLED; return IRQ_HANDLED;
printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x " pr_err("%s: unhandled interrupt status=0x%04x en=0x%04x "
"pio=%d\n", mmc_hostname(host->mmc), intr_status, "pio=%d\n", mmc_hostname(host->mmc), intr_status,
host->intr_en, host->pio_size); host->intr_en, host->pio_size);
return IRQ_NONE; return IRQ_NONE;
@ -505,9 +505,9 @@ static void mvsd_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
mrq = host->mrq; mrq = host->mrq;
if (mrq) { if (mrq) {
printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n", pr_err("%s: Timeout waiting for hardware interrupt.\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x " pr_err("%s: hw_state=0x%04x, intr_status=0x%04x "
"intr_en=0x%04x\n", mmc_hostname(host->mmc), "intr_en=0x%04x\n", mmc_hostname(host->mmc),
mvsd_read(MVSD_HW_STATE), mvsd_read(MVSD_HW_STATE),
mvsd_read(MVSD_NOR_INTR_STATUS), mvsd_read(MVSD_NOR_INTR_STATUS),
@ -762,7 +762,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host); ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
if (ret) { if (ret) {
printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq); pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq);
goto out; goto out;
} else } else
host->irq = irq; host->irq = irq;
@ -802,7 +802,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
if (ret) if (ret)
goto out; goto out;
printk(KERN_NOTICE "%s: %s driver initialized, ", pr_notice("%s: %s driver initialized, ",
mmc_hostname(mmc), DRIVER_NAME); mmc_hostname(mmc), DRIVER_NAME);
if (host->gpio_card_detect) if (host->gpio_card_detect)
printk("using GPIO %d for card detection\n", printk("using GPIO %d for card detection\n",

View File

@ -842,7 +842,7 @@ static int mxcmci_probe(struct platform_device *pdev)
int ret = 0, irq; int ret = 0, irq;
dma_cap_mask_t mask; dma_cap_mask_t mask;
printk(KERN_INFO "i.MX SDHC driver\n"); pr_info("i.MX SDHC driver\n");
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);

View File

@ -37,6 +37,7 @@
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/module.h>
#include <mach/mxs.h> #include <mach/mxs.h>
#include <mach/common.h> #include <mach/common.h>

View File

@ -450,15 +450,14 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
* framework is fixed, we need a workaround like this * framework is fixed, we need a workaround like this
* (which is safe for MMC, but not in general). * (which is safe for MMC, but not in general).
*/ */
if (regulator_is_enabled(host->vcc) > 0) { if (regulator_is_enabled(host->vcc) > 0 ||
regulator_enable(host->vcc); (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
regulator_disable(host->vcc); int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
}
if (host->vcc_aux) { mmc_slot(host).set_power(host->dev, host->slot_id,
if (regulator_is_enabled(reg) > 0) { 1, vdd);
regulator_enable(reg); mmc_slot(host).set_power(host->dev, host->slot_id,
regulator_disable(reg); 0, 0);
}
} }
} }
@ -1264,14 +1263,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
host->reqs_blocked = 0; host->reqs_blocked = 0;
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
if (host->protect_card) { if (host->protect_card) {
printk(KERN_INFO "%s: cover is closed, " pr_info("%s: cover is closed, "
"card is now accessible\n", "card is now accessible\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
host->protect_card = 0; host->protect_card = 0;
} }
} else { } else {
if (!host->protect_card) { if (!host->protect_card) {
printk(KERN_INFO "%s: cover is open, " pr_info"%s: cover is open, "
"card is now inaccessible\n", "card is now inaccessible\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
host->protect_card = 1; host->protect_card = 1;
@ -1422,7 +1421,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
if (!next && data->host_cookie && if (!next && data->host_cookie &&
data->host_cookie != host->next_data.cookie) { data->host_cookie != host->next_data.cookie) {
printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d" pr_warning("[%s] invalid cookie: data->host_cookie %d"
" host->next_data.cookie %d\n", " host->next_data.cookie %d\n",
__func__, data->host_cookie, host->next_data.cookie); __func__, data->host_cookie, host->next_data.cookie);
data->host_cookie = 0; data->host_cookie = 0;
@ -1943,6 +1942,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host); omap_hsmmc_context_save(host);
mmc->caps |= MMC_CAP_DISABLE; mmc->caps |= MMC_CAP_DISABLE;
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
}
pm_runtime_enable(host->dev); pm_runtime_enable(host->dev);
pm_runtime_get_sync(host->dev); pm_runtime_get_sync(host->dev);
@ -2015,7 +2018,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
} }
/* Request IRQ for MMC operations */ /* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED, ret = request_irq(host->irq, omap_hsmmc_irq, 0,
mmc_hostname(mmc), host); mmc_hostname(mmc), host);
if (ret) { if (ret) {
dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
@ -2043,8 +2046,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if ((mmc_slot(host).card_detect_irq)) { if ((mmc_slot(host).card_detect_irq)) {
ret = request_irq(mmc_slot(host).card_detect_irq, ret = request_irq(mmc_slot(host).card_detect_irq,
omap_hsmmc_cd_handler, omap_hsmmc_cd_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
| IRQF_DISABLED,
mmc_hostname(mmc), host); mmc_hostname(mmc), host);
if (ret) { if (ret) {
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),

View File

@ -558,7 +558,7 @@ static void pxamci_dma_irq(int dma, void *devid)
if (dcsr & DCSR_ENDINTR) { if (dcsr & DCSR_ENDINTR) {
writel(BUF_PART_FULL, host->base + MMC_PRTBUF); writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
} else { } else {
printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", pr_err("%s: DMA error on channel %d (DCSR=%#x)\n",
mmc_hostname(host->mmc), dma, dcsr); mmc_hostname(host->mmc), dma, dcsr);
host->data->error = -EIO; host->data->error = -EIO;
pxamci_data_done(host, 0); pxamci_data_done(host, 0);

View File

@ -247,7 +247,7 @@ static void s3cmci_check_sdio_irq(struct s3cmci_host *host)
{ {
if (host->sdio_irqen) { if (host->sdio_irqen) {
if (gpio_get_value(S3C2410_GPE(8)) == 0) { if (gpio_get_value(S3C2410_GPE(8)) == 0) {
printk(KERN_DEBUG "%s: signalling irq\n", __func__); pr_debug("%s: signalling irq\n", __func__);
mmc_signal_sdio_irq(host->mmc); mmc_signal_sdio_irq(host->mmc);
} }
} }
@ -344,7 +344,7 @@ static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
local_irq_save(flags); local_irq_save(flags);
//printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer); /* pr_debug("%s: transfer %d\n", __func__, transfer); */
host->irq_disabled = transfer; host->irq_disabled = transfer;

View File

@ -32,6 +32,15 @@
/* VENDOR SPEC register */ /* VENDOR SPEC register */
#define SDHCI_VENDOR_SPEC 0xC0 #define SDHCI_VENDOR_SPEC 0xC0
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002 #define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
#define SDHCI_MIX_CTRL 0x48
/*
* There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC:
* Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
* but bit28 is used as the INT DMA ERR in fsl eSDHC design.
* Define this macro DMA error INT for fsl eSDHC
*/
#define SDHCI_INT_VENDOR_SPEC_DMA_ERR 0x10000000
/* /*
* The CMDTYPE of the CMD register (offset 0xE) should be set to * The CMDTYPE of the CMD register (offset 0xE) should be set to
@ -51,6 +60,7 @@ enum imx_esdhc_type {
IMX35_ESDHC, IMX35_ESDHC,
IMX51_ESDHC, IMX51_ESDHC,
IMX53_ESDHC, IMX53_ESDHC,
IMX6Q_USDHC,
}; };
struct pltfm_imx_data { struct pltfm_imx_data {
@ -73,6 +83,9 @@ static struct platform_device_id imx_esdhc_devtype[] = {
}, { }, {
.name = "sdhci-esdhc-imx53", .name = "sdhci-esdhc-imx53",
.driver_data = IMX53_ESDHC, .driver_data = IMX53_ESDHC,
}, {
.name = "sdhci-usdhc-imx6q",
.driver_data = IMX6Q_USDHC,
}, { }, {
/* sentinel */ /* sentinel */
} }
@ -84,6 +97,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], }, { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], }, { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], }, { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
{ .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
@ -108,6 +122,11 @@ static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
return data->devtype == IMX53_ESDHC; return data->devtype == IMX53_ESDHC;
} }
static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
{
return data->devtype == IMX6Q_USDHC;
}
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
{ {
void __iomem *base = host->ioaddr + (reg & ~0x3); void __iomem *base = host->ioaddr + (reg & ~0x3);
@ -135,6 +154,27 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
val |= SDHCI_CARD_PRESENT; val |= SDHCI_CARD_PRESENT;
} }
if (unlikely(reg == SDHCI_CAPABILITIES)) {
/* In FSL esdhc IC module, only bit20 is used to indicate the
* ADMA2 capability of esdhc, but this bit is messed up on
* some SOCs (e.g. on MX25, MX35 this bit is set, but they
* don't actually support ADMA2). So set the BROKEN_ADMA
* uirk on MX25/35 platforms.
*/
if (val & SDHCI_CAN_DO_ADMA1) {
val &= ~SDHCI_CAN_DO_ADMA1;
val |= SDHCI_CAN_DO_ADMA2;
}
}
if (unlikely(reg == SDHCI_INT_STATUS)) {
if (val & SDHCI_INT_VENDOR_SPEC_DMA_ERR) {
val &= ~SDHCI_INT_VENDOR_SPEC_DMA_ERR;
val |= SDHCI_INT_ADMA_ERROR;
}
}
return val; return val;
} }
@ -179,13 +219,28 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
writel(v, host->ioaddr + SDHCI_VENDOR_SPEC); writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
} }
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
if (val & SDHCI_INT_ADMA_ERROR) {
val &= ~SDHCI_INT_ADMA_ERROR;
val |= SDHCI_INT_VENDOR_SPEC_DMA_ERR;
}
}
writel(val, host->ioaddr + reg); writel(val, host->ioaddr + reg);
} }
static u16 esdhc_readw_le(struct sdhci_host *host, int reg) static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
{ {
if (unlikely(reg == SDHCI_HOST_VERSION)) if (unlikely(reg == SDHCI_HOST_VERSION)) {
reg ^= 2; u16 val = readw(host->ioaddr + (reg ^ 2));
/*
* uSDHC supports SDHCI v3.0, but it's encoded as value
* 0x3 in host controller version register, which violates
* SDHCI_SPEC_300 definition. Work it around here.
*/
if ((val & SDHCI_SPEC_VER_MASK) == 3)
return --val;
}
return readw(host->ioaddr + reg); return readw(host->ioaddr + reg);
} }
@ -216,8 +271,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
if ((host->cmd->opcode == MMC_STOP_TRANSMISSION) if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)
&& (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) && (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
val |= SDHCI_CMD_ABORTCMD; val |= SDHCI_CMD_ABORTCMD;
writel(val << 16 | imx_data->scratchpad,
host->ioaddr + SDHCI_TRANSFER_MODE); if (is_imx6q_usdhc(imx_data)) {
u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL);
m = imx_data->scratchpad | (m & 0xffff0000);
writel(m, host->ioaddr + SDHCI_MIX_CTRL);
writel(val << 16,
host->ioaddr + SDHCI_TRANSFER_MODE);
} else {
writel(val << 16 | imx_data->scratchpad,
host->ioaddr + SDHCI_TRANSFER_MODE);
}
return; return;
case SDHCI_BLOCK_SIZE: case SDHCI_BLOCK_SIZE:
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
@ -311,9 +375,10 @@ static struct sdhci_ops sdhci_esdhc_ops = {
}; };
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
| SDHCI_QUIRK_BROKEN_CARD_DETECTION, | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
/* ADMA has issues. Might be fixable */
.ops = &sdhci_esdhc_ops, .ops = &sdhci_esdhc_ops,
}; };
@ -405,7 +470,8 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK; host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
| SDHCI_QUIRK_BROKEN_ADMA;
if (is_imx53_esdhc(imx_data)) if (is_imx53_esdhc(imx_data))
imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;

View File

@ -1,7 +1,7 @@
/* /*
* Freescale eSDHC controller driver. * Freescale eSDHC controller driver.
* *
* Copyright (c) 2007 Freescale Semiconductor, Inc. * Copyright (c) 2007, 2010 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc. * Copyright (c) 2009 MontaVista Software, Inc.
* *
* Authors: Xiaobo Xie <X.Xie@freescale.com> * Authors: Xiaobo Xie <X.Xie@freescale.com>
@ -15,6 +15,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "sdhci-esdhc.h" #include "sdhci-esdhc.h"
@ -22,11 +23,21 @@
static u16 esdhc_readw(struct sdhci_host *host, int reg) static u16 esdhc_readw(struct sdhci_host *host, int reg)
{ {
u16 ret; u16 ret;
int base = reg & ~0x3;
int shift = (reg & 0x2) * 8;
if (unlikely(reg == SDHCI_HOST_VERSION)) if (unlikely(reg == SDHCI_HOST_VERSION))
ret = in_be16(host->ioaddr + reg); ret = in_be32(host->ioaddr + base) & 0xffff;
else else
ret = sdhci_be32bs_readw(host, reg); ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff;
return ret;
}
static u8 esdhc_readb(struct sdhci_host *host, int reg)
{
int base = reg & ~0x3;
int shift = (reg & 0x3) * 8;
u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
return ret; return ret;
} }
@ -74,7 +85,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
static struct sdhci_ops sdhci_esdhc_ops = { static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = sdhci_be32bs_readl, .read_l = sdhci_be32bs_readl,
.read_w = esdhc_readw, .read_w = esdhc_readw,
.read_b = sdhci_be32bs_readb, .read_b = esdhc_readb,
.write_l = sdhci_be32bs_writel, .write_l = sdhci_be32bs_writel,
.write_w = esdhc_writew, .write_w = esdhc_writew,
.write_b = esdhc_writeb, .write_b = esdhc_writeb,

View File

@ -20,6 +20,7 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"

View File

@ -14,6 +14,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -21,6 +22,9 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio.h>
#include <linux/sfi.h>
#include <linux/pm_runtime.h>
#include "sdhci.h" #include "sdhci.h"
@ -43,6 +47,7 @@ struct sdhci_pci_slot;
struct sdhci_pci_fixes { struct sdhci_pci_fixes {
unsigned int quirks; unsigned int quirks;
bool allow_runtime_pm;
int (*probe) (struct sdhci_pci_chip *); int (*probe) (struct sdhci_pci_chip *);
@ -59,12 +64,16 @@ struct sdhci_pci_slot {
struct sdhci_host *host; struct sdhci_host *host;
int pci_bar; int pci_bar;
int rst_n_gpio;
int cd_gpio;
int cd_irq;
}; };
struct sdhci_pci_chip { struct sdhci_pci_chip {
struct pci_dev *pdev; struct pci_dev *pdev;
unsigned int quirks; unsigned int quirks;
bool allow_runtime_pm;
const struct sdhci_pci_fixes *fixes; const struct sdhci_pci_fixes *fixes;
int num_slots; /* Slots on controller */ int num_slots; /* Slots on controller */
@ -163,12 +172,129 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
return 0; return 0;
} }
/* Medfield eMMC hardware reset GPIOs */
static int mfd_emmc0_rst_gpio = -EINVAL;
static int mfd_emmc1_rst_gpio = -EINVAL;
static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
{
struct sfi_table_simple *sb = (struct sfi_table_simple *)table;
struct sfi_gpio_table_entry *entry;
int i, num;
num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
entry = (struct sfi_gpio_table_entry *)sb->pentry;
for (i = 0; i < num; i++, entry++) {
if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN))
mfd_emmc0_rst_gpio = entry->pin_no;
else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN))
mfd_emmc1_rst_gpio = entry->pin_no;
}
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
{
struct sdhci_pci_slot *slot = dev_id;
struct sdhci_host *host = slot->host;
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
#define MFLD_SD_CD_PIN 69
static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
{
int err, irq, gpio = MFLD_SD_CD_PIN;
slot->cd_gpio = -EINVAL;
slot->cd_irq = -EINVAL;
err = gpio_request(gpio, "sd_cd");
if (err < 0)
goto out;
err = gpio_direction_input(gpio);
if (err < 0)
goto out_free;
irq = gpio_to_irq(gpio);
if (irq < 0)
goto out_free;
err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING, "sd_cd", slot);
if (err)
goto out_free;
slot->cd_gpio = gpio;
slot->cd_irq = irq;
slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION;
return 0;
out_free:
gpio_free(gpio);
out:
dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
return 0;
}
static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead)
{
if (slot->cd_irq >= 0)
free_irq(slot->cd_irq, slot);
gpio_free(slot->cd_gpio);
}
#else
#define mfd_sd_probe_slot NULL
#define mfd_sd_remove_slot NULL
#endif
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
{ {
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; const char *name = NULL;
int gpio = -EINVAL;
sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse);
switch (slot->chip->pdev->device) {
case PCI_DEVICE_ID_INTEL_MFD_EMMC0:
gpio = mfd_emmc0_rst_gpio;
name = "eMMC0_reset";
break;
case PCI_DEVICE_ID_INTEL_MFD_EMMC1:
gpio = mfd_emmc1_rst_gpio;
name = "eMMC1_reset";
break;
}
if (!gpio_request(gpio, name)) {
gpio_direction_output(gpio, 1);
slot->rst_n_gpio = gpio;
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
}
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
return 0; return 0;
} }
static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
{
gpio_free(slot->rst_n_gpio);
}
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
.probe_slot = mrst_hc_probe_slot, .probe_slot = mrst_hc_probe_slot,
@ -181,15 +307,21 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
.probe_slot = mfd_sd_probe_slot,
.remove_slot = mfd_sd_remove_slot,
}; };
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
}; };
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
.probe_slot = mfd_emmc_probe_slot, .probe_slot = mfd_emmc_probe_slot,
.remove_slot = mfd_emmc_remove_slot,
}; };
/* O2Micro extra registers */ /* O2Micro extra registers */
@ -832,9 +964,25 @@ static int sdhci_pci_8bit_width(struct sdhci_host *host, int width)
return 0; return 0;
} }
static void sdhci_pci_hw_reset(struct sdhci_host *host)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
int rst_n_gpio = slot->rst_n_gpio;
if (!gpio_is_valid(rst_n_gpio))
return;
gpio_set_value_cansleep(rst_n_gpio, 0);
/* For eMMC, minimum is 1us but give it 10us for good measure */
udelay(10);
gpio_set_value_cansleep(rst_n_gpio, 1);
/* For eMMC, minimum is 200us but give it 300us for good measure */
usleep_range(300, 1000);
}
static struct sdhci_ops sdhci_pci_ops = { static struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma, .enable_dma = sdhci_pci_enable_dma,
.platform_8bit_width = sdhci_pci_8bit_width, .platform_8bit_width = sdhci_pci_8bit_width,
.hw_reset = sdhci_pci_hw_reset,
}; };
/*****************************************************************************\ /*****************************************************************************\
@ -944,6 +1092,95 @@ static int sdhci_pci_resume(struct pci_dev *pdev)
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
static int sdhci_pci_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
pm_message_t state = { .event = PM_EVENT_SUSPEND };
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_runtime_suspend_host(slot->host);
if (ret) {
for (i--; i >= 0; i--)
sdhci_runtime_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_runtime_resume_host(chip->slots[i]->host);
return ret;
}
}
return 0;
}
static int sdhci_pci_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
int i, ret;
chip = pci_get_drvdata(pdev);
if (!chip)
return 0;
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_runtime_resume_host(slot->host);
if (ret)
return ret;
}
return 0;
}
static int sdhci_pci_runtime_idle(struct device *dev)
{
return 0;
}
#else
#define sdhci_pci_runtime_suspend NULL
#define sdhci_pci_runtime_resume NULL
#define sdhci_pci_runtime_idle NULL
#endif
static const struct dev_pm_ops sdhci_pci_pm_ops = {
.runtime_suspend = sdhci_pci_runtime_suspend,
.runtime_resume = sdhci_pci_runtime_resume,
.runtime_idle = sdhci_pci_runtime_idle,
};
/*****************************************************************************\ /*****************************************************************************\
* * * *
* Device probing/removal * * Device probing/removal *
@ -988,6 +1225,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
slot->chip = chip; slot->chip = chip;
slot->host = host; slot->host = host;
slot->pci_bar = bar; slot->pci_bar = bar;
slot->rst_n_gpio = -EINVAL;
host->hw_name = "PCI"; host->hw_name = "PCI";
host->ops = &sdhci_pci_ops; host->ops = &sdhci_pci_ops;
@ -1058,6 +1296,21 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_free_host(slot->host); sdhci_free_host(slot->host);
} }
static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev)
{
pm_runtime_put_noidle(dev);
pm_runtime_allow(dev);
pm_runtime_set_autosuspend_delay(dev, 50);
pm_runtime_use_autosuspend(dev);
pm_suspend_ignore_children(dev, 1);
}
static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev)
{
pm_runtime_forbid(dev);
pm_runtime_get_noresume(dev);
}
static int __devinit sdhci_pci_probe(struct pci_dev *pdev, static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent) const struct pci_device_id *ent)
{ {
@ -1107,8 +1360,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->pdev = pdev; chip->pdev = pdev;
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
if (chip->fixes) if (chip->fixes) {
chip->quirks = chip->fixes->quirks; chip->quirks = chip->fixes->quirks;
chip->allow_runtime_pm = chip->fixes->allow_runtime_pm;
}
chip->num_slots = slots; chip->num_slots = slots;
pci_set_drvdata(pdev, chip); pci_set_drvdata(pdev, chip);
@ -1133,6 +1388,9 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->slots[i] = slot; chip->slots[i] = slot;
} }
if (chip->allow_runtime_pm)
sdhci_pci_runtime_pm_allow(&pdev->dev);
return 0; return 0;
free: free:
@ -1152,6 +1410,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
chip = pci_get_drvdata(pdev); chip = pci_get_drvdata(pdev);
if (chip) { if (chip) {
if (chip->allow_runtime_pm)
sdhci_pci_runtime_pm_forbid(&pdev->dev);
for (i = 0; i < chip->num_slots; i++) for (i = 0; i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]); sdhci_pci_remove_slot(chip->slots[i]);
@ -1169,6 +1430,9 @@ static struct pci_driver sdhci_driver = {
.remove = __devexit_p(sdhci_pci_remove), .remove = __devexit_p(sdhci_pci_remove),
.suspend = sdhci_pci_suspend, .suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume, .resume = sdhci_pci_resume,
.driver = {
.pm = &sdhci_pci_pm_ops
},
}; };
/*****************************************************************************\ /*****************************************************************************\

View File

@ -29,6 +29,7 @@
*/ */
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#ifdef CONFIG_PPC #ifdef CONFIG_PPC
#include <asm/machdep.h> #include <asm/machdep.h>

View File

@ -21,6 +21,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/module.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
@ -59,7 +60,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
* tune timing of read data/command when crc error happen * tune timing of read data/command when crc error happen
* no performance impact * no performance impact
*/ */
if (pdata->clk_delay_sel == 1) { if (pdata && pdata->clk_delay_sel == 1) {
tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); tmp = readw(host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
@ -71,7 +72,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); writew(tmp, host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP);
} }
if (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING) { if (pdata && (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING)) {
tmp = readw(host->ioaddr + SD_FIFO_PARAM); tmp = readw(host->ioaddr + SD_FIFO_PARAM);
tmp &= ~CLK_GATE_SETTING_BITS; tmp &= ~CLK_GATE_SETTING_BITS;
writew(tmp, host->ioaddr + SD_FIFO_PARAM); writew(tmp, host->ioaddr + SD_FIFO_PARAM);

View File

@ -27,6 +27,7 @@
#include <linux/platform_data/pxa_sdhci.h> #include <linux/platform_data/pxa_sdhci.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h>
#include "sdhci.h" #include "sdhci.h"
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"

View File

@ -203,17 +203,23 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
} }
/* reconfigure the hardware for new clock rate */ /* reprogram default hardware configuration */
writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
host->ioaddr + S3C64XX_SDHCI_CONTROL4);
{ ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
struct mmc_ios ios; ctrl |= (S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR |
S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK |
S3C_SDHCI_CTRL2_ENFBCLKRX |
S3C_SDHCI_CTRL2_DFCNT_NONE |
S3C_SDHCI_CTRL2_ENCLKOUTHOLD);
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
ios.clock = clock; /* reconfigure the controller for new clock rate */
ctrl = (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0);
if (ourhost->pdata->cfg_card) if (clock < 25 * 1000000)
(ourhost->pdata->cfg_card)(ourhost->pdev, host->ioaddr, ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);
&ios, NULL); writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3);
}
} }
/** /**
@ -561,8 +567,10 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
err_req_regs: err_req_regs:
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
clk_disable(sc->clk_bus[ptr]); if (sc->clk_bus[ptr]) {
clk_put(sc->clk_bus[ptr]); clk_disable(sc->clk_bus[ptr]);
clk_put(sc->clk_bus[ptr]);
}
} }
err_no_busclks: err_no_busclks:

View File

@ -17,6 +17,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -177,8 +178,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
sdhci->data->card_power_gpio); sdhci->data->card_power_gpio);
goto err_pgpio_direction; goto err_pgpio_direction;
} }
gpio_set_value(sdhci->data->card_power_gpio, 1);
} }
if (sdhci->data->card_int_gpio >= 0) { if (sdhci->data->card_int_gpio >= 0) {

View File

@ -17,9 +17,12 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/module.h>
#include <asm/gpio.h> #include <asm/gpio.h>
@ -75,10 +78,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
{ {
struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc)); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct tegra_sdhci_platform_data *plat; struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
plat = pdev->dev.platform_data;
if (!gpio_is_valid(plat->wp_gpio)) if (!gpio_is_valid(plat->wp_gpio))
return -1; return -1;
@ -96,12 +97,10 @@ static irqreturn_t carddetect_irq(int irq, void *data)
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
{ {
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct tegra_sdhci_platform_data *plat; struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
u32 ctrl; u32 ctrl;
plat = pdev->dev.platform_data;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) { if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS; ctrl &= ~SDHCI_CTRL_4BITBUS;
@ -133,6 +132,36 @@ static struct sdhci_pltfm_data sdhci_tegra_pdata = {
.ops = &tegra_sdhci_ops, .ops = &tegra_sdhci_ops,
}; };
static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
{ .compatible = "nvidia,tegra20-sdhci", },
{}
};
MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
struct platform_device *pdev)
{
struct tegra_sdhci_platform_data *plat;
struct device_node *np = pdev->dev.of_node;
if (!np)
return NULL;
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat) {
dev_err(&pdev->dev, "Can't allocate platform data\n");
return NULL;
}
plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
if (of_find_property(np, "support-8bit", NULL))
plat->is_8bit = 1;
return plat;
}
static int __devinit sdhci_tegra_probe(struct platform_device *pdev) static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
{ {
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
@ -149,12 +178,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
plat = pdev->dev.platform_data; plat = pdev->dev.platform_data;
if (plat == NULL)
plat = sdhci_tegra_dt_parse_pdata(pdev);
if (plat == NULL) { if (plat == NULL) {
dev_err(mmc_dev(host->mmc), "missing platform data\n"); dev_err(mmc_dev(host->mmc), "missing platform data\n");
rc = -ENXIO; rc = -ENXIO;
goto err_no_plat; goto err_no_plat;
} }
pltfm_host->priv = plat;
if (gpio_is_valid(plat->power_gpio)) { if (gpio_is_valid(plat->power_gpio)) {
rc = gpio_request(plat->power_gpio, "sdhci_power"); rc = gpio_request(plat->power_gpio, "sdhci_power");
if (rc) { if (rc) {
@ -249,13 +283,11 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
{ {
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct tegra_sdhci_platform_data *plat; struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead); sdhci_remove_host(host, dead);
plat = pdev->dev.platform_data;
if (gpio_is_valid(plat->wp_gpio)) { if (gpio_is_valid(plat->wp_gpio)) {
tegra_gpio_disable(plat->wp_gpio); tegra_gpio_disable(plat->wp_gpio);
gpio_free(plat->wp_gpio); gpio_free(plat->wp_gpio);
@ -284,6 +316,7 @@ static struct platform_driver sdhci_tegra_driver = {
.driver = { .driver = {
.name = "sdhci-tegra", .name = "sdhci-tegra",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = sdhci_tegra_dt_match,
}, },
.probe = sdhci_tegra_probe, .probe = sdhci_tegra_probe,
.remove = __devexit_p(sdhci_tegra_remove), .remove = __devexit_p(sdhci_tegra_remove),

View File

@ -16,10 +16,12 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -41,6 +43,7 @@
#define MAX_TUNING_LOOP 40 #define MAX_TUNING_LOOP 40
static unsigned int debug_quirks = 0; static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *); static void sdhci_finish_data(struct sdhci_host *);
@ -49,53 +52,67 @@ static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc); static int sdhci_execute_tuning(struct mmc_host *mmc);
static void sdhci_tuning_timer(unsigned long data); static void sdhci_tuning_timer(unsigned long data);
#ifdef CONFIG_PM_RUNTIME
static int sdhci_runtime_pm_get(struct sdhci_host *host);
static int sdhci_runtime_pm_put(struct sdhci_host *host);
#else
static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
{
return 0;
}
static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
{
return 0;
}
#endif
static void sdhci_dumpregs(struct sdhci_host *host) static void sdhci_dumpregs(struct sdhci_host *host)
{ {
printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
sdhci_readl(host, SDHCI_DMA_ADDRESS), sdhci_readl(host, SDHCI_DMA_ADDRESS),
sdhci_readw(host, SDHCI_HOST_VERSION)); sdhci_readw(host, SDHCI_HOST_VERSION));
printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
sdhci_readw(host, SDHCI_BLOCK_SIZE), sdhci_readw(host, SDHCI_BLOCK_SIZE),
sdhci_readw(host, SDHCI_BLOCK_COUNT)); sdhci_readw(host, SDHCI_BLOCK_COUNT));
printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
sdhci_readl(host, SDHCI_ARGUMENT), sdhci_readl(host, SDHCI_ARGUMENT),
sdhci_readw(host, SDHCI_TRANSFER_MODE)); sdhci_readw(host, SDHCI_TRANSFER_MODE));
printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
sdhci_readl(host, SDHCI_PRESENT_STATE), sdhci_readl(host, SDHCI_PRESENT_STATE),
sdhci_readb(host, SDHCI_HOST_CONTROL)); sdhci_readb(host, SDHCI_HOST_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
sdhci_readb(host, SDHCI_POWER_CONTROL), sdhci_readb(host, SDHCI_POWER_CONTROL),
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
sdhci_readw(host, SDHCI_CLOCK_CONTROL)); sdhci_readw(host, SDHCI_CLOCK_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
sdhci_readl(host, SDHCI_INT_STATUS)); sdhci_readl(host, SDHCI_INT_STATUS));
printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
sdhci_readl(host, SDHCI_INT_ENABLE), sdhci_readl(host, SDHCI_INT_ENABLE),
sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
sdhci_readw(host, SDHCI_ACMD12_ERR), sdhci_readw(host, SDHCI_ACMD12_ERR),
sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
sdhci_readl(host, SDHCI_CAPABILITIES), sdhci_readl(host, SDHCI_CAPABILITIES),
sdhci_readl(host, SDHCI_CAPABILITIES_1)); sdhci_readl(host, SDHCI_CAPABILITIES_1));
printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
sdhci_readw(host, SDHCI_COMMAND), sdhci_readw(host, SDHCI_COMMAND),
sdhci_readl(host, SDHCI_MAX_CURRENT)); sdhci_readl(host, SDHCI_MAX_CURRENT));
printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n", pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
sdhci_readw(host, SDHCI_HOST_CONTROL2)); sdhci_readw(host, SDHCI_HOST_CONTROL2));
if (host->flags & SDHCI_USE_ADMA) if (host->flags & SDHCI_USE_ADMA)
printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
readl(host->ioaddr + SDHCI_ADMA_ERROR), readl(host->ioaddr + SDHCI_ADMA_ERROR),
readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); pr_debug(DRIVER_NAME ": ===========================================\n");
} }
/*****************************************************************************\ /*****************************************************************************\
@ -132,6 +149,9 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
return; return;
if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
return;
present = sdhci_readl(host, SDHCI_PRESENT_STATE) & present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT; SDHCI_CARD_PRESENT;
irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
@ -180,7 +200,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
/* hw clears the bit when it's done */ /* hw clears the bit when it's done */
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) { if (timeout == 0) {
printk(KERN_ERR "%s: Reset 0x%x never completed.\n", pr_err("%s: Reset 0x%x never completed.\n",
mmc_hostname(host->mmc), (int)mask); mmc_hostname(host->mmc), (int)mask);
sdhci_dumpregs(host); sdhci_dumpregs(host);
return; return;
@ -251,11 +271,14 @@ static void sdhci_led_control(struct led_classdev *led,
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
if (host->runtime_suspended)
goto out;
if (brightness == LED_OFF) if (brightness == LED_OFF)
sdhci_deactivate_led(host); sdhci_deactivate_led(host);
else else
sdhci_activate_led(host); sdhci_activate_led(host);
out:
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
#endif #endif
@ -654,7 +677,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
} }
if (count >= 0xF) { if (count >= 0xF) {
printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n", pr_warning("%s: Too large timeout requested for CMD%d!\n",
mmc_hostname(host->mmc), cmd->opcode); mmc_hostname(host->mmc), cmd->opcode);
count = 0xE; count = 0xE;
} }
@ -949,7 +972,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) { if (timeout == 0) {
printk(KERN_ERR "%s: Controller never released " pr_err("%s: Controller never released "
"inhibit bit(s).\n", mmc_hostname(host->mmc)); "inhibit bit(s).\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host); sdhci_dumpregs(host);
cmd->error = -EIO; cmd->error = -EIO;
@ -971,7 +994,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_set_transfer_mode(host, cmd); sdhci_set_transfer_mode(host, cmd);
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
printk(KERN_ERR "%s: Unsupported response type!\n", pr_err("%s: Unsupported response type!\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
cmd->error = -EINVAL; cmd->error = -EINVAL;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
@ -1121,7 +1144,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) { & SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) { if (timeout == 0) {
printk(KERN_ERR "%s: Internal clock never " pr_err("%s: Internal clock never "
"stabilised.\n", mmc_hostname(host->mmc)); "stabilised.\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host); sdhci_dumpregs(host);
return; return;
@ -1209,6 +1232,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host = mmc_priv(mmc); host = mmc_priv(mmc);
sdhci_runtime_pm_get(host);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->mrq != NULL); WARN_ON(host->mrq != NULL);
@ -1269,14 +1294,11 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{ {
struct sdhci_host *host;
unsigned long flags; unsigned long flags;
u8 ctrl; u8 ctrl;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
if (host->flags & SDHCI_DEVICE_DEAD) if (host->flags & SDHCI_DEVICE_DEAD)
@ -1426,7 +1448,16 @@ out:
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static int check_ro(struct sdhci_host *host) static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
sdhci_runtime_pm_get(host);
sdhci_do_set_ios(host, ios);
sdhci_runtime_pm_put(host);
}
static int sdhci_check_ro(struct sdhci_host *host)
{ {
unsigned long flags; unsigned long flags;
int is_readonly; int is_readonly;
@ -1450,19 +1481,16 @@ static int check_ro(struct sdhci_host *host)
#define SAMPLE_COUNT 5 #define SAMPLE_COUNT 5
static int sdhci_get_ro(struct mmc_host *mmc) static int sdhci_do_get_ro(struct sdhci_host *host)
{ {
struct sdhci_host *host;
int i, ro_count; int i, ro_count;
host = mmc_priv(mmc);
if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT)) if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
return check_ro(host); return sdhci_check_ro(host);
ro_count = 0; ro_count = 0;
for (i = 0; i < SAMPLE_COUNT; i++) { for (i = 0; i < SAMPLE_COUNT; i++) {
if (check_ro(host)) { if (sdhci_check_ro(host)) {
if (++ro_count > SAMPLE_COUNT / 2) if (++ro_count > SAMPLE_COUNT / 2)
return 1; return 1;
} }
@ -1471,38 +1499,64 @@ static int sdhci_get_ro(struct mmc_host *mmc)
return 0; return 0;
} }
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) static void sdhci_hw_reset(struct mmc_host *mmc)
{ {
struct sdhci_host *host; struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
host = mmc_priv(mmc); if (host->ops && host->ops->hw_reset)
host->ops->hw_reset(host);
}
spin_lock_irqsave(&host->lock, flags); static int sdhci_get_ro(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
int ret;
sdhci_runtime_pm_get(host);
ret = sdhci_do_get_ro(host);
sdhci_runtime_pm_put(host);
return ret;
}
static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
{
if (host->flags & SDHCI_DEVICE_DEAD) if (host->flags & SDHCI_DEVICE_DEAD)
goto out; goto out;
if (enable)
host->flags |= SDHCI_SDIO_IRQ_ENABLED;
else
host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
/* SDIO IRQ will be enabled as appropriate in runtime resume */
if (host->runtime_suspended)
goto out;
if (enable) if (enable)
sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
else else
sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
out: out:
mmiowb(); mmiowb();
}
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
sdhci_enable_sdio_irq_nolock(host, enable);
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
struct mmc_ios *ios) struct mmc_ios *ios)
{ {
struct sdhci_host *host;
u8 pwr; u8 pwr;
u16 clk, ctrl; u16 clk, ctrl;
u32 present_state; u32 present_state;
host = mmc_priv(mmc);
/* /*
* Signal Voltage Switching is only applicable for Host Controllers * Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above. * v3.00 and above.
@ -1528,7 +1582,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
if (!(ctrl & SDHCI_CTRL_VDD_180)) if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0; return 0;
else { else {
printk(KERN_INFO DRIVER_NAME ": Switching to 3.3V " pr_info(DRIVER_NAME ": Switching to 3.3V "
"signalling voltage failed\n"); "signalling voltage failed\n");
return -EIO; return -EIO;
} }
@ -1587,7 +1641,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
pwr |= SDHCI_POWER_ON; pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
printk(KERN_INFO DRIVER_NAME ": Switching to 1.8V signalling " pr_info(DRIVER_NAME ": Switching to 1.8V signalling "
"voltage failed, retrying with S18R set to 0\n"); "voltage failed, retrying with S18R set to 0\n");
return -EAGAIN; return -EAGAIN;
} else } else
@ -1595,6 +1649,20 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return 0; return 0;
} }
static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
int err;
if (host->version < SDHCI_SPEC_300)
return 0;
sdhci_runtime_pm_get(host);
err = sdhci_do_start_signal_voltage_switch(host, ios);
sdhci_runtime_pm_put(host);
return err;
}
static int sdhci_execute_tuning(struct mmc_host *mmc) static int sdhci_execute_tuning(struct mmc_host *mmc)
{ {
struct sdhci_host *host; struct sdhci_host *host;
@ -1606,6 +1674,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
host = mmc_priv(mmc); host = mmc_priv(mmc);
sdhci_runtime_pm_get(host);
disable_irq(host->irq); disable_irq(host->irq);
spin_lock(&host->lock); spin_lock(&host->lock);
@ -1623,6 +1692,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
else { else {
spin_unlock(&host->lock); spin_unlock(&host->lock);
enable_irq(host->irq); enable_irq(host->irq);
sdhci_runtime_pm_put(host);
return 0; return 0;
} }
@ -1648,7 +1718,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
timeout = 150; timeout = 150;
do { do {
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
struct mmc_request mrq = {0}; struct mmc_request mrq = {NULL};
if (!tuning_loop_counter && !timeout) if (!tuning_loop_counter && !timeout)
break; break;
@ -1694,7 +1764,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
spin_lock(&host->lock); spin_lock(&host->lock);
if (!host->tuning_done) { if (!host->tuning_done) {
printk(KERN_INFO DRIVER_NAME ": Timeout waiting for " pr_info(DRIVER_NAME ": Timeout waiting for "
"Buffer Read Ready interrupt during tuning " "Buffer Read Ready interrupt during tuning "
"procedure, falling back to fixed sampling " "procedure, falling back to fixed sampling "
"clock\n"); "clock\n");
@ -1724,7 +1794,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
} else { } else {
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
printk(KERN_INFO DRIVER_NAME ": Tuning procedure" pr_info(DRIVER_NAME ": Tuning procedure"
" failed, falling back to fixed sampling" " failed, falling back to fixed sampling"
" clock\n"); " clock\n");
err = -EIO; err = -EIO;
@ -1766,18 +1836,16 @@ out:
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
spin_unlock(&host->lock); spin_unlock(&host->lock);
enable_irq(host->irq); enable_irq(host->irq);
sdhci_runtime_pm_put(host);
return err; return err;
} }
static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable) static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
{ {
struct sdhci_host *host;
u16 ctrl; u16 ctrl;
unsigned long flags; unsigned long flags;
host = mmc_priv(mmc);
/* Host Controller v3.00 defines preset value registers */ /* Host Controller v3.00 defines preset value registers */
if (host->version < SDHCI_SPEC_300) if (host->version < SDHCI_SPEC_300)
return; return;
@ -1793,18 +1861,30 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
host->flags |= SDHCI_PV_ENABLED;
} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
host->flags &= ~SDHCI_PV_ENABLED;
} }
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
{
struct sdhci_host *host = mmc_priv(mmc);
sdhci_runtime_pm_get(host);
sdhci_do_enable_preset_value(host, enable);
sdhci_runtime_pm_put(host);
}
static const struct mmc_host_ops sdhci_ops = { static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request, .request = sdhci_request,
.set_ios = sdhci_set_ios, .set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro, .get_ro = sdhci_get_ro,
.hw_reset = sdhci_hw_reset,
.enable_sdio_irq = sdhci_enable_sdio_irq, .enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning, .execute_tuning = sdhci_execute_tuning,
@ -1826,19 +1906,19 @@ static void sdhci_tasklet_card(unsigned long param)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { /* Check host->mrq first in case we are runtime suspended */
if (host->mrq) { if (host->mrq &&
printk(KERN_ERR "%s: Card removed during transfer!\n", !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
mmc_hostname(host->mmc)); pr_err("%s: Card removed during transfer!\n",
printk(KERN_ERR "%s: Resetting controller.\n", mmc_hostname(host->mmc));
mmc_hostname(host->mmc)); pr_err("%s: Resetting controller.\n",
mmc_hostname(host->mmc));
sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA); sdhci_reset(host, SDHCI_RESET_DATA);
host->mrq->cmd->error = -ENOMEDIUM; host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
}
} }
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
@ -1854,14 +1934,16 @@ static void sdhci_tasklet_finish(unsigned long param)
host = (struct sdhci_host*)param; host = (struct sdhci_host*)param;
spin_lock_irqsave(&host->lock, flags);
/* /*
* If this tasklet gets rescheduled while running, it will * If this tasklet gets rescheduled while running, it will
* be run again afterwards but without any active request. * be run again afterwards but without any active request.
*/ */
if (!host->mrq) if (!host->mrq) {
spin_unlock_irqrestore(&host->lock, flags);
return; return;
}
spin_lock_irqsave(&host->lock, flags);
del_timer(&host->timer); del_timer(&host->timer);
@ -1905,6 +1987,7 @@ static void sdhci_tasklet_finish(unsigned long param)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(host->mmc, mrq); mmc_request_done(host->mmc, mrq);
sdhci_runtime_pm_put(host);
} }
static void sdhci_timeout_timer(unsigned long data) static void sdhci_timeout_timer(unsigned long data)
@ -1917,7 +2000,7 @@ static void sdhci_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
if (host->mrq) { if (host->mrq) {
printk(KERN_ERR "%s: Timeout waiting for hardware " pr_err("%s: Timeout waiting for hardware "
"interrupt.\n", mmc_hostname(host->mmc)); "interrupt.\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host); sdhci_dumpregs(host);
@ -1963,7 +2046,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
BUG_ON(intmask == 0); BUG_ON(intmask == 0);
if (!host->cmd) { if (!host->cmd) {
printk(KERN_ERR "%s: Got command interrupt 0x%08x even " pr_err("%s: Got command interrupt 0x%08x even "
"though no command operation was in progress.\n", "though no command operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask); mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host); sdhci_dumpregs(host);
@ -2063,7 +2146,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
} }
} }
printk(KERN_ERR "%s: Got data interrupt 0x%08x even " pr_err("%s: Got data interrupt 0x%08x even "
"though no data operation was in progress.\n", "though no data operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask); mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host); sdhci_dumpregs(host);
@ -2080,7 +2163,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
!= MMC_BUS_TEST_R) != MMC_BUS_TEST_R)
host->data->error = -EILSEQ; host->data->error = -EILSEQ;
else if (intmask & SDHCI_INT_ADMA_ERROR) { else if (intmask & SDHCI_INT_ADMA_ERROR) {
printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
sdhci_show_adma_error(host); sdhci_show_adma_error(host);
host->data->error = -EIO; host->data->error = -EIO;
} }
@ -2136,12 +2219,19 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
static irqreturn_t sdhci_irq(int irq, void *dev_id) static irqreturn_t sdhci_irq(int irq, void *dev_id)
{ {
irqreturn_t result; irqreturn_t result;
struct sdhci_host* host = dev_id; struct sdhci_host *host = dev_id;
u32 intmask; u32 intmask;
int cardint = 0; int cardint = 0;
spin_lock(&host->lock); spin_lock(&host->lock);
if (host->runtime_suspended) {
spin_unlock(&host->lock);
pr_warning("%s: got irq while runtime suspended\n",
mmc_hostname(host->mmc));
return IRQ_HANDLED;
}
intmask = sdhci_readl(host, SDHCI_INT_STATUS); intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) { if (!intmask || intmask == 0xffffffff) {
@ -2194,7 +2284,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_ERROR; intmask &= ~SDHCI_INT_ERROR;
if (intmask & SDHCI_INT_BUS_POWER) { if (intmask & SDHCI_INT_BUS_POWER) {
printk(KERN_ERR "%s: Card is consuming too much power!\n", pr_err("%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
} }
@ -2207,7 +2297,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_CARD_INT; intmask &= ~SDHCI_INT_CARD_INT;
if (intmask) { if (intmask) {
printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", pr_err("%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), intmask); mmc_hostname(host->mmc), intmask);
sdhci_dumpregs(host); sdhci_dumpregs(host);
@ -2275,7 +2365,6 @@ int sdhci_resume_host(struct sdhci_host *host)
return ret; return ret;
} }
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) if (host->ops->enable_dma)
host->ops->enable_dma(host); host->ops->enable_dma(host);
@ -2314,6 +2403,90 @@ EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
static int sdhci_runtime_pm_get(struct sdhci_host *host)
{
return pm_runtime_get_sync(host->mmc->parent);
}
static int sdhci_runtime_pm_put(struct sdhci_host *host)
{
pm_runtime_mark_last_busy(host->mmc->parent);
return pm_runtime_put_autosuspend(host->mmc->parent);
}
int sdhci_runtime_suspend_host(struct sdhci_host *host)
{
unsigned long flags;
int ret = 0;
/* Disable tuning since we are suspending */
if (host->version >= SDHCI_SPEC_300 &&
host->tuning_mode == SDHCI_TUNING_MODE_1) {
del_timer_sync(&host->tuning_timer);
host->flags &= ~SDHCI_NEEDS_RETUNING;
}
spin_lock_irqsave(&host->lock, flags);
sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
spin_unlock_irqrestore(&host->lock, flags);
synchronize_irq(host->irq);
spin_lock_irqsave(&host->lock, flags);
host->runtime_suspended = true;
spin_unlock_irqrestore(&host->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
int sdhci_runtime_resume_host(struct sdhci_host *host)
{
unsigned long flags;
int ret = 0, host_flags = host->flags;
if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
}
sdhci_init(host, 0);
/* Force clock and power re-program */
host->pwr = 0;
host->clock = 0;
sdhci_do_set_ios(host, &host->mmc->ios);
sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
if (host_flags & SDHCI_PV_ENABLED)
sdhci_do_enable_preset_value(host, true);
/* Set the re-tuning expiration flag */
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
(host->tuning_mode == SDHCI_TUNING_MODE_1))
host->flags |= SDHCI_NEEDS_RETUNING;
spin_lock_irqsave(&host->lock, flags);
host->runtime_suspended = false;
/* Enable SDIO IRQ */
if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
sdhci_enable_sdio_irq_nolock(host, true);
/* Enable Card Detection */
sdhci_enable_card_detection(host);
spin_unlock_irqrestore(&host->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
#endif
/*****************************************************************************\ /*****************************************************************************\
* * * *
* Device allocation/registration * * Device allocation/registration *
@ -2356,6 +2529,8 @@ int sdhci_add_host(struct sdhci_host *host)
if (debug_quirks) if (debug_quirks)
host->quirks = debug_quirks; host->quirks = debug_quirks;
if (debug_quirks2)
host->quirks2 = debug_quirks2;
sdhci_reset(host, SDHCI_RESET_ALL); sdhci_reset(host, SDHCI_RESET_ALL);
@ -2363,7 +2538,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->version = (host->version & SDHCI_SPEC_VER_MASK) host->version = (host->version & SDHCI_SPEC_VER_MASK)
>> SDHCI_SPEC_VER_SHIFT; >> SDHCI_SPEC_VER_SHIFT;
if (host->version > SDHCI_SPEC_300) { if (host->version > SDHCI_SPEC_300) {
printk(KERN_ERR "%s: Unknown controller version (%d). " pr_err("%s: Unknown controller version (%d). "
"You may experience problems.\n", mmc_hostname(mmc), "You may experience problems.\n", mmc_hostname(mmc),
host->version); host->version);
} }
@ -2400,7 +2575,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) { if (host->ops->enable_dma) {
if (host->ops->enable_dma(host)) { if (host->ops->enable_dma(host)) {
printk(KERN_WARNING "%s: No suitable DMA " pr_warning("%s: No suitable DMA "
"available. Falling back to PIO.\n", "available. Falling back to PIO.\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
host->flags &= host->flags &=
@ -2420,7 +2595,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (!host->adma_desc || !host->align_buffer) { if (!host->adma_desc || !host->align_buffer) {
kfree(host->adma_desc); kfree(host->adma_desc);
kfree(host->align_buffer); kfree(host->align_buffer);
printk(KERN_WARNING "%s: Unable to allocate ADMA " pr_warning("%s: Unable to allocate ADMA "
"buffers. Falling back to standard DMA.\n", "buffers. Falling back to standard DMA.\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA; host->flags &= ~SDHCI_USE_ADMA;
@ -2448,8 +2623,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->max_clk == 0 || host->quirks & if (host->max_clk == 0 || host->quirks &
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) { SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
if (!host->ops->get_max_clock) { if (!host->ops->get_max_clock) {
printk(KERN_ERR pr_err("%s: Hardware doesn't specify base clock "
"%s: Hardware doesn't specify base clock "
"frequency.\n", mmc_hostname(mmc)); "frequency.\n", mmc_hostname(mmc));
return -ENODEV; return -ENODEV;
} }
@ -2495,8 +2669,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->timeout_clk = host->ops->get_timeout_clock(host); host->timeout_clk = host->ops->get_timeout_clock(host);
} else if (!(host->quirks & } else if (!(host->quirks &
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
printk(KERN_ERR pr_err("%s: Hardware doesn't specify timeout clock "
"%s: Hardware doesn't specify timeout clock "
"frequency.\n", mmc_hostname(mmc)); "frequency.\n", mmc_hostname(mmc));
return -ENODEV; return -ENODEV;
} }
@ -2566,6 +2739,15 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_DRIVER_TYPE_D) if (caps[1] & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D; mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
/*
* If Power Off Notify capability is enabled by the host,
* set notify to short power off notify timeout value.
*/
if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
else
mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
/* Initial value for re-tuning timer count */ /* Initial value for re-tuning timer count */
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
SDHCI_RETUNING_TIMER_COUNT_SHIFT; SDHCI_RETUNING_TIMER_COUNT_SHIFT;
@ -2655,7 +2837,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->ocr_avail_mmc &= host->ocr_avail_mmc; mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
if (mmc->ocr_avail == 0) { if (mmc->ocr_avail == 0) {
printk(KERN_ERR "%s: Hardware doesn't report any " pr_err("%s: Hardware doesn't report any "
"support voltages.\n", mmc_hostname(mmc)); "support voltages.\n", mmc_hostname(mmc));
return -ENODEV; return -ENODEV;
} }
@ -2703,7 +2885,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >> mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT; SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) { if (mmc->max_blk_size >= 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, " pr_warning("%s: Invalid maximum block size, "
"assuming 512 bytes\n", mmc_hostname(mmc)); "assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 0; mmc->max_blk_size = 0;
} }
@ -2742,7 +2924,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) { if (IS_ERR(host->vmmc)) {
printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc)); pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL; host->vmmc = NULL;
} else { } else {
regulator_enable(host->vmmc); regulator_enable(host->vmmc);
@ -2771,7 +2953,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc_add_host(mmc); mmc_add_host(mmc);
printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n", pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ? "ADMA" : (host->flags & SDHCI_USE_ADMA) ? "ADMA" :
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
@ -2804,7 +2986,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
host->flags |= SDHCI_DEVICE_DEAD; host->flags |= SDHCI_DEVICE_DEAD;
if (host->mrq) { if (host->mrq) {
printk(KERN_ERR "%s: Controller removed during " pr_err("%s: Controller removed during "
" transfer!\n", mmc_hostname(host->mmc)); " transfer!\n", mmc_hostname(host->mmc));
host->mrq->cmd->error = -ENOMEDIUM; host->mrq->cmd->error = -ENOMEDIUM;
@ -2863,9 +3045,9 @@ EXPORT_SYMBOL_GPL(sdhci_free_host);
static int __init sdhci_drv_init(void) static int __init sdhci_drv_init(void)
{ {
printk(KERN_INFO DRIVER_NAME pr_info(DRIVER_NAME
": Secure Digital Host Controller Interface driver\n"); ": Secure Digital Host Controller Interface driver\n");
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
return 0; return 0;
} }
@ -2878,9 +3060,11 @@ module_init(sdhci_drv_init);
module_exit(sdhci_drv_exit); module_exit(sdhci_drv_exit);
module_param(debug_quirks, uint, 0444); module_param(debug_quirks, uint, 0444);
module_param(debug_quirks2, uint, 0444);
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>"); MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver"); MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
MODULE_PARM_DESC(debug_quirks2, "Force certain other quirks.");

View File

@ -273,7 +273,7 @@ struct sdhci_ops {
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host);
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@ -379,4 +379,9 @@ extern int sdhci_resume_host(struct sdhci_host *host);
extern void sdhci_enable_irq_wakeups(struct sdhci_host *host); extern void sdhci_enable_irq_wakeups(struct sdhci_host *host);
#endif #endif
#ifdef CONFIG_PM_RUNTIME
extern int sdhci_runtime_suspend_host(struct sdhci_host *host);
extern int sdhci_runtime_resume_host(struct sdhci_host *host);
#endif
#endif /* __SDHCI_HW_H */ #endif /* __SDHCI_HW_H */

View File

@ -26,6 +26,7 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>

View File

@ -31,6 +31,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/module.h>
#define DRIVER_NAME "sh_mmcif" #define DRIVER_NAME "sh_mmcif"
#define DRIVER_VERSION "2010-04-28" #define DRIVER_VERSION "2010-04-28"
@ -165,6 +166,8 @@ struct sh_mmcif_host {
struct mmc_host *mmc; struct mmc_host *mmc;
struct mmc_data *data; struct mmc_data *data;
struct platform_device *pd; struct platform_device *pd;
struct sh_dmae_slave dma_slave_tx;
struct sh_dmae_slave dma_slave_rx;
struct clk *hclk; struct clk *hclk;
unsigned int clk; unsigned int clk;
int bus_width; int bus_width;
@ -323,25 +326,35 @@ static bool sh_mmcif_filter(struct dma_chan *chan, void *arg)
static void sh_mmcif_request_dma(struct sh_mmcif_host *host, static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata) struct sh_mmcif_plat_data *pdata)
{ {
struct sh_dmae_slave *tx, *rx;
host->dma_active = false; host->dma_active = false;
/* We can only either use DMA for both Tx and Rx or not use it at all */ /* We can only either use DMA for both Tx and Rx or not use it at all */
if (pdata->dma) { if (pdata->dma) {
dev_warn(&host->pd->dev,
"Update your platform to use embedded DMA slave IDs\n");
tx = &pdata->dma->chan_priv_tx;
rx = &pdata->dma->chan_priv_rx;
} else {
tx = &host->dma_slave_tx;
tx->slave_id = pdata->slave_id_tx;
rx = &host->dma_slave_rx;
rx->slave_id = pdata->slave_id_rx;
}
if (tx->slave_id > 0 && rx->slave_id > 0) {
dma_cap_mask_t mask; dma_cap_mask_t mask;
dma_cap_zero(mask); dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_SLAVE, mask);
host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, tx);
&pdata->dma->chan_priv_tx);
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx); host->chan_tx);
if (!host->chan_tx) if (!host->chan_tx)
return; return;
host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, rx);
&pdata->dma->chan_priv_rx);
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx); host->chan_rx);

View File

@ -21,6 +21,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/sh_mobile_sdhi.h> #include <linux/mmc/sh_mobile_sdhi.h>
@ -96,7 +97,8 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
struct tmio_mmc_host *host; struct tmio_mmc_host *host;
char clk_name[8]; char clk_name[8];
int i, irq, ret; int irq, ret, i = 0;
bool multiplexed_isr = true;
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) { if (priv == NULL) {
@ -153,27 +155,60 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
goto eprobe; goto eprobe;
for (i = 0; i < 3; i++) { /*
irq = platform_get_irq(pdev, i); * Allow one or more specific (named) ISRs or
if (irq < 0) { * one or more multiplexed (un-named) ISRs.
if (i) { */
continue;
} else { irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
ret = irq; if (irq >= 0) {
goto eirq; multiplexed_isr = false;
} ret = request_irq(irq, tmio_mmc_card_detect_irq, 0,
}
ret = request_irq(irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) { if (ret)
while (i--) { goto eirq_card_detect;
irq = platform_get_irq(pdev, i);
if (irq >= 0)
free_irq(irq, host);
}
goto eirq;
}
} }
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
if (irq >= 0) {
multiplexed_isr = false;
ret = request_irq(irq, tmio_mmc_sdio_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
goto eirq_sdio;
}
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
if (irq >= 0) {
multiplexed_isr = false;
ret = request_irq(irq, tmio_mmc_sdcard_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
goto eirq_sdcard;
} else if (!multiplexed_isr) {
dev_err(&pdev->dev,
"Principal SD-card IRQ is missing among named interrupts\n");
ret = irq;
goto eirq_sdcard;
}
if (multiplexed_isr) {
while (1) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
break;
i++;
ret = request_irq(irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
goto eirq_multiplexed;
}
/* There must be at least one IRQ source */
if (!i)
goto eirq_multiplexed;
}
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long) mmc_hostname(host->mmc), (unsigned long)
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start), (platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
@ -181,7 +216,20 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
return ret; return ret;
eirq: eirq_multiplexed:
while (i--) {
irq = platform_get_irq(pdev, i);
free_irq(irq, host);
}
eirq_sdcard:
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
if (irq >= 0)
free_irq(irq, host);
eirq_sdio:
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
if (irq >= 0)
free_irq(irq, host);
eirq_card_detect:
tmio_mmc_host_remove(host); tmio_mmc_host_remove(host);
eprobe: eprobe:
clk_disable(priv->clk); clk_disable(priv->clk);
@ -197,16 +245,17 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
int i, irq; int i = 0, irq;
p->pdata = NULL; p->pdata = NULL;
tmio_mmc_host_remove(host); tmio_mmc_host_remove(host);
for (i = 0; i < 3; i++) { while (1) {
irq = platform_get_irq(pdev, i); irq = platform_get_irq(pdev, i++);
if (irq >= 0) if (irq < 0)
free_irq(irq, host); break;
free_irq(irq, host);
} }
clk_disable(priv->clk); clk_disable(priv->clk);

View File

@ -16,6 +16,7 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/module.h>
#include <asm/io.h> #include <asm/io.h>
#define DRIVER_NAME "tifm_sd" #define DRIVER_NAME "tifm_sd"
@ -631,7 +632,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
} }
if (host->req) { if (host->req) {
printk(KERN_ERR "%s : unfinished request detected\n", pr_err("%s : unfinished request detected\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
mrq->cmd->error = -ETIMEDOUT; mrq->cmd->error = -ETIMEDOUT;
goto err_out; goto err_out;
@ -671,7 +672,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
r_data->flags & MMC_DATA_WRITE r_data->flags & MMC_DATA_WRITE
? PCI_DMA_TODEVICE ? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE)) { : PCI_DMA_FROMDEVICE)) {
printk(KERN_ERR "%s : scatterlist map failed\n", pr_err("%s : scatterlist map failed\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
mrq->cmd->error = -ENOMEM; mrq->cmd->error = -ENOMEM;
goto err_out; goto err_out;
@ -683,7 +684,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
? PCI_DMA_TODEVICE ? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE); : PCI_DMA_FROMDEVICE);
if (host->sg_len < 1) { if (host->sg_len < 1) {
printk(KERN_ERR "%s : scatterlist map failed\n", pr_err("%s : scatterlist map failed\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
tifm_unmap_sg(sock, &host->bounce_buf, 1, tifm_unmap_sg(sock, &host->bounce_buf, 1,
r_data->flags & MMC_DATA_WRITE r_data->flags & MMC_DATA_WRITE
@ -747,7 +748,7 @@ static void tifm_sd_end_cmd(unsigned long data)
host->req = NULL; host->req = NULL;
if (!mrq) { if (!mrq) {
printk(KERN_ERR " %s : no request to complete?\n", pr_err(" %s : no request to complete?\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
spin_unlock_irqrestore(&sock->lock, flags); spin_unlock_irqrestore(&sock->lock, flags);
return; return;
@ -786,8 +787,7 @@ static void tifm_sd_abort(unsigned long data)
{ {
struct tifm_sd *host = (struct tifm_sd*)data; struct tifm_sd *host = (struct tifm_sd*)data;
printk(KERN_ERR pr_err("%s : card failed to respond for a long period of time "
"%s : card failed to respond for a long period of time "
"(%x, %x)\n", "(%x, %x)\n",
dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags); dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags);
@ -905,7 +905,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
} }
if (rc) { if (rc) {
printk(KERN_ERR "%s : controller failed to reset\n", pr_err("%s : controller failed to reset\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
return -ENODEV; return -ENODEV;
} }
@ -931,8 +931,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host)
} }
if (rc) { if (rc) {
printk(KERN_ERR pr_err("%s : card not ready - probe failed on initialization\n",
"%s : card not ready - probe failed on initialization\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
return -ENODEV; return -ENODEV;
} }
@ -953,7 +952,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
if (!(TIFM_SOCK_STATE_OCCUPIED if (!(TIFM_SOCK_STATE_OCCUPIED
& readl(sock->addr + SOCK_PRESENT_STATE))) { & readl(sock->addr + SOCK_PRESENT_STATE))) {
printk(KERN_WARNING "%s : card gone, unexpectedly\n", pr_warning("%s : card gone, unexpectedly\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
return rc; return rc;
} }

View File

@ -88,8 +88,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
if (ret) if (ret)
goto cell_disable; goto cell_disable;
ret = request_irq(irq, tmio_mmc_irq, IRQF_DISABLED | ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) if (ret)
goto host_remove; goto host_remove;

View File

@ -79,6 +79,10 @@ struct tmio_mmc_host {
struct delayed_work delayed_reset_work; struct delayed_work delayed_reset_work;
struct work_struct done; struct work_struct done;
/* Cache IRQ mask */
u32 sdcard_irq_mask;
u32 sdio_irq_mask;
spinlock_t lock; /* protect host private data */ spinlock_t lock; /* protect host private data */
unsigned long last_req_ts; unsigned long last_req_ts;
struct mutex ios_lock; /* protect set_ios() context */ struct mutex ios_lock; /* protect set_ios() context */
@ -93,6 +97,9 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i); void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i); void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid); irqreturn_t tmio_mmc_irq(int irq, void *devid);
irqreturn_t tmio_mmc_sdcard_irq(int irq, void *devid);
irqreturn_t tmio_mmc_card_detect_irq(int irq, void *devid);
irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid);
static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg, static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
unsigned long *flags) unsigned long *flags)

View File

@ -48,14 +48,14 @@
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i) void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
{ {
u32 mask = sd_ctrl_read32(host, CTL_IRQ_MASK) & ~(i & TMIO_MASK_IRQ); host->sdcard_irq_mask &= ~(i & TMIO_MASK_IRQ);
sd_ctrl_write32(host, CTL_IRQ_MASK, mask); sd_ctrl_write32(host, CTL_IRQ_MASK, host->sdcard_irq_mask);
} }
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i) void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
{ {
u32 mask = sd_ctrl_read32(host, CTL_IRQ_MASK) | (i & TMIO_MASK_IRQ); host->sdcard_irq_mask |= (i & TMIO_MASK_IRQ);
sd_ctrl_write32(host, CTL_IRQ_MASK, mask); sd_ctrl_write32(host, CTL_IRQ_MASK, host->sdcard_irq_mask);
} }
static void tmio_mmc_ack_mmc_irqs(struct tmio_mmc_host *host, u32 i) static void tmio_mmc_ack_mmc_irqs(struct tmio_mmc_host *host, u32 i)
@ -92,7 +92,7 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host)
static void pr_debug_status(u32 status) static void pr_debug_status(u32 status)
{ {
int i = 0; int i = 0;
printk(KERN_DEBUG "status: %08x = ", status); pr_debug("status: %08x = ", status);
STATUS_TO_TEXT(CARD_REMOVE, status, i); STATUS_TO_TEXT(CARD_REMOVE, status, i);
STATUS_TO_TEXT(CARD_INSERT, status, i); STATUS_TO_TEXT(CARD_INSERT, status, i);
STATUS_TO_TEXT(SIGSTATE, status, i); STATUS_TO_TEXT(SIGSTATE, status, i);
@ -127,11 +127,13 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
if (enable) { if (enable) {
host->sdio_irq_enabled = 1; host->sdio_irq_enabled = 1;
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
~TMIO_SDIO_STAT_IOIRQ;
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
(TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ));
} else { } else {
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, TMIO_SDIO_MASK_ALL); host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
host->sdio_irq_enabled = 0; host->sdio_irq_enabled = 0;
} }
@ -543,45 +545,20 @@ out:
spin_unlock(&host->lock); spin_unlock(&host->lock);
} }
irqreturn_t tmio_mmc_irq(int irq, void *devid) static void tmio_mmc_card_irq_status(struct tmio_mmc_host *host,
int *ireg, int *status)
{
*status = sd_ctrl_read32(host, CTL_STATUS);
*ireg = *status & TMIO_MASK_IRQ & ~host->sdcard_irq_mask;
pr_debug_status(*status);
pr_debug_status(*ireg);
}
static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host,
int ireg, int status)
{ {
struct tmio_mmc_host *host = devid;
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
struct tmio_mmc_data *pdata = host->pdata;
unsigned int ireg, irq_mask, status;
unsigned int sdio_ireg, sdio_irq_mask, sdio_status;
pr_debug("MMC IRQ begin\n");
status = sd_ctrl_read32(host, CTL_STATUS);
irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
ireg = status & TMIO_MASK_IRQ & ~irq_mask;
sdio_ireg = 0;
if (!ireg && pdata->flags & TMIO_MMC_SDIO_IRQ) {
sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
sdio_irq_mask = sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK);
sdio_ireg = sdio_status & TMIO_SDIO_MASK_ALL & ~sdio_irq_mask;
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status & ~TMIO_SDIO_MASK_ALL);
if (sdio_ireg && !host->sdio_irq_enabled) {
pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n",
sdio_status, sdio_irq_mask, sdio_ireg);
tmio_mmc_enable_sdio_irq(mmc, 0);
goto out;
}
if (mmc->caps & MMC_CAP_SDIO_IRQ &&
sdio_ireg & TMIO_SDIO_STAT_IOIRQ)
mmc_signal_sdio_irq(mmc);
if (sdio_ireg)
goto out;
}
pr_debug_status(status);
pr_debug_status(ireg);
/* Card insert / remove attempts */ /* Card insert / remove attempts */
if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) { if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
@ -591,43 +568,102 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid)
((ireg & TMIO_STAT_CARD_INSERT) && !mmc->card)) && ((ireg & TMIO_STAT_CARD_INSERT) && !mmc->card)) &&
!work_pending(&mmc->detect.work)) !work_pending(&mmc->detect.work))
mmc_detect_change(host->mmc, msecs_to_jiffies(100)); mmc_detect_change(host->mmc, msecs_to_jiffies(100));
goto out; return true;
} }
/* CRC and other errors */ return false;
/* if (ireg & TMIO_STAT_ERR_IRQ) }
* handled |= tmio_error_irq(host, irq, stat);
*/
irqreturn_t tmio_mmc_card_detect_irq(int irq, void *devid)
{
unsigned int ireg, status;
struct tmio_mmc_host *host = devid;
tmio_mmc_card_irq_status(host, &ireg, &status);
__tmio_mmc_card_detect_irq(host, ireg, status);
return IRQ_HANDLED;
}
EXPORT_SYMBOL(tmio_mmc_card_detect_irq);
static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host,
int ireg, int status)
{
/* Command completion */ /* Command completion */
if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) { if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) {
tmio_mmc_ack_mmc_irqs(host, tmio_mmc_ack_mmc_irqs(host,
TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDRESPEND |
TMIO_STAT_CMDTIMEOUT); TMIO_STAT_CMDTIMEOUT);
tmio_mmc_cmd_irq(host, status); tmio_mmc_cmd_irq(host, status);
goto out; return true;
} }
/* Data transfer */ /* Data transfer */
if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) { if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ); tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
tmio_mmc_pio_irq(host); tmio_mmc_pio_irq(host);
goto out; return true;
} }
/* Data transfer completion */ /* Data transfer completion */
if (ireg & TMIO_STAT_DATAEND) { if (ireg & TMIO_STAT_DATAEND) {
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND); tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
tmio_mmc_data_irq(host); tmio_mmc_data_irq(host);
goto out; return true;
} }
pr_warning("tmio_mmc: Spurious irq, disabling! " return false;
"0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg); }
pr_debug_status(status);
tmio_mmc_disable_mmc_irqs(host, status & ~irq_mask); irqreturn_t tmio_mmc_sdcard_irq(int irq, void *devid)
{
unsigned int ireg, status;
struct tmio_mmc_host *host = devid;
tmio_mmc_card_irq_status(host, &ireg, &status);
__tmio_mmc_sdcard_irq(host, ireg, status);
return IRQ_HANDLED;
}
EXPORT_SYMBOL(tmio_mmc_sdcard_irq);
irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
{
struct tmio_mmc_host *host = devid;
struct mmc_host *mmc = host->mmc;
struct tmio_mmc_data *pdata = host->pdata;
unsigned int ireg, status;
if (!(pdata->flags & TMIO_MMC_SDIO_IRQ))
return IRQ_HANDLED;
status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask;
sd_ctrl_write16(host, CTL_SDIO_STATUS, status & ~TMIO_SDIO_MASK_ALL);
if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ)
mmc_signal_sdio_irq(mmc);
return IRQ_HANDLED;
}
EXPORT_SYMBOL(tmio_mmc_sdio_irq);
irqreturn_t tmio_mmc_irq(int irq, void *devid)
{
struct tmio_mmc_host *host = devid;
unsigned int ireg, status;
pr_debug("MMC IRQ begin\n");
tmio_mmc_card_irq_status(host, &ireg, &status);
if (__tmio_mmc_card_detect_irq(host, ireg, status))
return IRQ_HANDLED;
if (__tmio_mmc_sdcard_irq(host, ireg, status))
return IRQ_HANDLED;
tmio_mmc_sdio_irq(irq, devid);
out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
EXPORT_SYMBOL(tmio_mmc_irq); EXPORT_SYMBOL(tmio_mmc_irq);
@ -882,6 +918,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
tmio_mmc_clk_stop(_host); tmio_mmc_clk_stop(_host);
tmio_mmc_reset(_host); tmio_mmc_reset(_host);
_host->sdcard_irq_mask = sd_ctrl_read32(_host, CTL_IRQ_MASK);
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL); tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
if (pdata->flags & TMIO_MMC_SDIO_IRQ) if (pdata->flags & TMIO_MMC_SDIO_IRQ)
tmio_mmc_enable_sdio_irq(mmc, 0); tmio_mmc_enable_sdio_irq(mmc, 0);

View File

@ -9,6 +9,7 @@
*/ */
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -1191,7 +1192,7 @@ static void __devexit via_sd_remove(struct pci_dev *pcidev)
mmiowb(); mmiowb();
if (sdhost->mrq) { if (sdhost->mrq) {
printk(KERN_ERR "%s: Controller removed during " pr_err("%s: Controller removed during "
"transfer\n", mmc_hostname(sdhost->mmc)); "transfer\n", mmc_hostname(sdhost->mmc));
/* make sure all DMA is stopped */ /* make sure all DMA is stopped */

View File

@ -194,7 +194,7 @@ static void wbsd_reset(struct wbsd_host *host)
{ {
u8 setup; u8 setup;
printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc)); pr_err("%s: Resetting chip\n", mmc_hostname(host->mmc));
/* /*
* Soft reset of chip (SD/MMC part). * Soft reset of chip (SD/MMC part).
@ -721,7 +721,7 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
* Any leftover data? * Any leftover data?
*/ */
if (count) { if (count) {
printk(KERN_ERR "%s: Incomplete DMA transfer. " pr_err("%s: Incomplete DMA transfer. "
"%d bytes left.\n", "%d bytes left.\n",
mmc_hostname(host->mmc), count); mmc_hostname(host->mmc), count);
@ -803,7 +803,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
default: default:
#ifdef CONFIG_MMC_DEBUG #ifdef CONFIG_MMC_DEBUG
printk(KERN_WARNING "%s: Data command %d is not " pr_warning("%s: Data command %d is not "
"supported by this controller.\n", "supported by this controller.\n",
mmc_hostname(host->mmc), cmd->opcode); mmc_hostname(host->mmc), cmd->opcode);
#endif #endif
@ -1029,7 +1029,7 @@ static void wbsd_tasklet_card(unsigned long param)
host->flags &= ~WBSD_FCARD_PRESENT; host->flags &= ~WBSD_FCARD_PRESENT;
if (host->mrq) { if (host->mrq) {
printk(KERN_ERR "%s: Card removed during transfer!\n", pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
wbsd_reset(host); wbsd_reset(host);
@ -1429,7 +1429,7 @@ free:
free_dma(dma); free_dma(dma);
err: err:
printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. " pr_warning(DRIVER_NAME ": Unable to allocate DMA %d. "
"Falling back on FIFO.\n", dma); "Falling back on FIFO.\n", dma);
} }
@ -1664,7 +1664,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
ret = wbsd_scan(host); ret = wbsd_scan(host);
if (ret) { if (ret) {
if (pnp && (ret == -ENODEV)) { if (pnp && (ret == -ENODEV)) {
printk(KERN_WARNING DRIVER_NAME pr_warning(DRIVER_NAME
": Unable to confirm device presence. You may " ": Unable to confirm device presence. You may "
"experience lock-ups.\n"); "experience lock-ups.\n");
} else { } else {
@ -1688,7 +1688,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
*/ */
if (pnp) { if (pnp) {
if ((host->config != 0) && !wbsd_chip_validate(host)) { if ((host->config != 0) && !wbsd_chip_validate(host)) {
printk(KERN_WARNING DRIVER_NAME pr_warning(DRIVER_NAME
": PnP active but chip not configured! " ": PnP active but chip not configured! "
"You probably have a buggy BIOS. " "You probably have a buggy BIOS. "
"Configuring chip manually.\n"); "Configuring chip manually.\n");
@ -1720,7 +1720,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
mmc_add_host(mmc); mmc_add_host(mmc);
printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); pr_info("%s: W83L51xD", mmc_hostname(mmc));
if (host->chip_id != 0) if (host->chip_id != 0)
printk(" id %x", (int)host->chip_id); printk(" id %x", (int)host->chip_id);
printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
@ -1909,7 +1909,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
*/ */
if (host->config != 0) { if (host->config != 0) {
if (!wbsd_chip_validate(host)) { if (!wbsd_chip_validate(host)) {
printk(KERN_WARNING DRIVER_NAME pr_warning(DRIVER_NAME
": PnP active but chip not configured! " ": PnP active but chip not configured! "
"You probably have a buggy BIOS. " "You probably have a buggy BIOS. "
"Configuring chip manually.\n"); "Configuring chip manually.\n");
@ -1973,9 +1973,9 @@ static int __init wbsd_drv_init(void)
{ {
int result; int result;
printk(KERN_INFO DRIVER_NAME pr_info(DRIVER_NAME
": Winbond W83L51xD SD/MMC card interface driver\n"); ": Winbond W83L51xD SD/MMC card interface driver\n");
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
#ifdef CONFIG_PNP #ifdef CONFIG_PNP

View File

@ -1,7 +1,7 @@
#ifndef __LINUX_ATMEL_MCI_H #ifndef __LINUX_ATMEL_MCI_H
#define __LINUX_ATMEL_MCI_H #define __LINUX_ATMEL_MCI_H
#define ATMEL_MCI_MAX_NR_SLOTS 2 #define ATMCI_MAX_NR_SLOTS 2
/** /**
* struct mci_slot_pdata - board-specific per-slot configuration * struct mci_slot_pdata - board-specific per-slot configuration
@ -33,7 +33,7 @@ struct mci_slot_pdata {
*/ */
struct mci_platform_data { struct mci_platform_data {
struct mci_dma_data *dma_slave; struct mci_dma_data *dma_slave;
struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS]; struct mci_slot_pdata slot[ATMCI_MAX_NR_SLOTS];
}; };
#endif /* __LINUX_ATMEL_MCI_H */ #endif /* __LINUX_ATMEL_MCI_H */

View File

@ -33,4 +33,6 @@
#define ATMEL_PDC_PTSR 0x124 /* Transfer Status Register */ #define ATMEL_PDC_PTSR 0x124 /* Transfer Status Register */
#define ATMEL_PDC_SCND_BUF_OFF 0x10 /* Offset between first and second buffer registers */
#endif #endif

View File

@ -50,8 +50,12 @@ struct mmc_ext_csd {
u8 rel_sectors; u8 rel_sectors;
u8 rel_param; u8 rel_param;
u8 part_config; u8 part_config;
u8 cache_ctrl;
u8 rst_n_function;
unsigned int part_time; /* Units: ms */ unsigned int part_time; /* Units: ms */
unsigned int sa_timeout; /* Units: 100ns */ unsigned int sa_timeout; /* Units: 100ns */
unsigned int generic_cmd6_time; /* Units: 10ms */
unsigned int power_off_longtime; /* Units: ms */
unsigned int hs_max_dtr; unsigned int hs_max_dtr;
unsigned int sectors; unsigned int sectors;
unsigned int card_type; unsigned int card_type;
@ -63,11 +67,15 @@ struct mmc_ext_csd {
bool enhanced_area_en; /* enable bit */ bool enhanced_area_en; /* enable bit */
unsigned long long enhanced_area_offset; /* Units: Byte */ unsigned long long enhanced_area_offset; /* Units: Byte */
unsigned int enhanced_area_size; /* Units: KB */ unsigned int enhanced_area_size; /* Units: KB */
unsigned int boot_size; /* in bytes */ unsigned int cache_size; /* Units: KB */
bool hpi_en; /* HPI enablebit */
bool hpi; /* HPI support bit */
unsigned int hpi_cmd; /* cmd used as HPI */
u8 raw_partition_support; /* 160 */ u8 raw_partition_support; /* 160 */
u8 raw_erased_mem_count; /* 181 */ u8 raw_erased_mem_count; /* 181 */
u8 raw_ext_csd_structure; /* 194 */ u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */ u8 raw_card_type; /* 196 */
u8 out_of_int_time; /* 198 */
u8 raw_s_a_timeout; /* 217 */ u8 raw_s_a_timeout; /* 217 */
u8 raw_hc_erase_gap_size; /* 221 */ u8 raw_hc_erase_gap_size; /* 221 */
u8 raw_erase_timeout_mult; /* 223 */ u8 raw_erase_timeout_mult; /* 223 */
@ -77,6 +85,9 @@ struct mmc_ext_csd {
u8 raw_sec_feature_support;/* 231 */ u8 raw_sec_feature_support;/* 231 */
u8 raw_trim_mult; /* 232 */ u8 raw_trim_mult; /* 232 */
u8 raw_sectors[4]; /* 212 - 4 bytes */ u8 raw_sectors[4]; /* 212 - 4 bytes */
unsigned int feature_support;
#define MMC_DISCARD_FEATURE BIT(0) /* CMD38 feature */
}; };
struct sd_scr { struct sd_scr {
@ -157,6 +168,24 @@ struct sdio_func_tuple;
#define SDIO_MAX_FUNCS 7 #define SDIO_MAX_FUNCS 7
/* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
#define MMC_NUM_GP_PARTITION 4
#define MMC_NUM_PHY_PARTITION 6
#define MAX_MMC_PART_NAME_LEN 20
/*
* MMC Physical partitions
*/
struct mmc_part {
unsigned int size; /* partition size (in bytes) */
unsigned int part_cfg; /* partition type */
char name[MAX_MMC_PART_NAME_LEN];
bool force_ro; /* to make boot parts RO by default */
};
/* /*
* MMC device * MMC device
*/ */
@ -188,6 +217,13 @@ struct mmc_card {
#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ #define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */ #define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */ #define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
/* byte mode */
unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION 0
#define MMC_POWERED_ON 1
#define MMC_POWEROFF_SHORT 2
#define MMC_POWEROFF_LONG 3
unsigned int erase_size; /* erase size in sectors */ unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */ unsigned int erase_shift; /* if erase unit is power 2 */
@ -216,8 +252,23 @@ struct mmc_card {
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
struct dentry *debugfs_root; struct dentry *debugfs_root;
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
unsigned int nr_parts;
}; };
/*
* This function fill contents in mmc_part.
*/
static inline void mmc_part_add(struct mmc_card *card, unsigned int size,
unsigned int part_cfg, char *name, int idx, bool ro)
{
card->part[card->nr_parts].size = size;
card->part[card->nr_parts].part_cfg = part_cfg;
sprintf(card->part[card->nr_parts].name, name, idx);
card->part[card->nr_parts].force_ro = ro;
card->nr_parts++;
}
/* /*
* The world is not perfect and supplies us with broken mmc/sdio devices. * The world is not perfect and supplies us with broken mmc/sdio devices.
* For at least some of these bugs we need a work-around. * For at least some of these bugs we need a work-around.
@ -377,6 +428,11 @@ static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF; return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
} }
static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_BROKEN_BYTE_MODE_512;
}
#define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_name(c) ((c)->cid.prod_name)
#define mmc_card_id(c) (dev_name(&(c)->dev)) #define mmc_card_id(c) (dev_name(&(c)->dev))

View File

@ -136,6 +136,7 @@ struct mmc_async_req;
extern struct mmc_async_req *mmc_start_req(struct mmc_host *, extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
struct mmc_async_req *, int *); struct mmc_async_req *, int *);
extern int mmc_interrupt_hpi(struct mmc_card *);
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
@ -146,6 +147,7 @@ extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
#define MMC_ERASE_ARG 0x00000000 #define MMC_ERASE_ARG 0x00000000
#define MMC_SECURE_ERASE_ARG 0x80000000 #define MMC_SECURE_ERASE_ARG 0x80000000
#define MMC_TRIM_ARG 0x00000001 #define MMC_TRIM_ARG 0x00000001
#define MMC_DISCARD_ARG 0x00000003
#define MMC_SECURE_TRIM1_ARG 0x80000001 #define MMC_SECURE_TRIM1_ARG 0x80000001
#define MMC_SECURE_TRIM2_ARG 0x80008000 #define MMC_SECURE_TRIM2_ARG 0x80008000
@ -156,12 +158,17 @@ extern int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
unsigned int arg); unsigned int arg);
extern int mmc_can_erase(struct mmc_card *card); extern int mmc_can_erase(struct mmc_card *card);
extern int mmc_can_trim(struct mmc_card *card); extern int mmc_can_trim(struct mmc_card *card);
extern int mmc_can_discard(struct mmc_card *card);
extern int mmc_can_sanitize(struct mmc_card *card);
extern int mmc_can_secure_erase_trim(struct mmc_card *card); extern int mmc_can_secure_erase_trim(struct mmc_card *card);
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
unsigned int nr); unsigned int nr);
extern unsigned int mmc_calc_max_discard(struct mmc_card *card); extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen); extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
extern int mmc_hw_reset(struct mmc_host *host);
extern int mmc_hw_reset_check(struct mmc_host *host);
extern int mmc_can_reset(struct mmc_card *card);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); 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 unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
@ -171,6 +178,8 @@ extern void mmc_release_host(struct mmc_host *host);
extern void mmc_do_release_host(struct mmc_host *host); extern void mmc_do_release_host(struct mmc_host *host);
extern int mmc_try_claim_host(struct mmc_host *host); extern int mmc_try_claim_host(struct mmc_host *host);
extern int mmc_flush_cache(struct mmc_card *);
/** /**
* mmc_claim_host - exclusively claim a host * mmc_claim_host - exclusively claim a host
* @host: mmc host to claim * @host: mmc host to claim

View File

@ -72,6 +72,8 @@ struct mmc_data;
* rate and timeout calculations. * rate and timeout calculations.
* @current_speed: Configured rate of the controller. * @current_speed: Configured rate of the controller.
* @num_slots: Number of slots available. * @num_slots: Number of slots available.
* @verid: Denote Version ID.
* @data_offset: Set the offset of DATA register according to VERID.
* @pdev: Platform device associated with the MMC controller. * @pdev: Platform device associated with the MMC controller.
* @pdata: Platform data associated with the MMC controller. * @pdata: Platform data associated with the MMC controller.
* @slot: Slots sharing this MMC controller. * @slot: Slots sharing this MMC controller.
@ -147,6 +149,8 @@ struct dw_mci {
u32 current_speed; u32 current_speed;
u32 num_slots; u32 num_slots;
u32 fifoth_val; u32 fifoth_val;
u16 verid;
u16 data_offset;
struct platform_device *pdev; struct platform_device *pdev;
struct dw_mci_board *pdata; struct dw_mci_board *pdata;
struct dw_mci_slot *slot[MAX_MCI_SLOTS]; struct dw_mci_slot *slot[MAX_MCI_SLOTS];

View File

@ -12,6 +12,7 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/fault-inject.h>
#include <linux/mmc/core.h> #include <linux/mmc/core.h>
#include <linux/mmc/pm.h> #include <linux/mmc/pm.h>
@ -108,6 +109,9 @@ struct mmc_host_ops {
* It is optional for the host to implement pre_req and post_req in * It is optional for the host to implement pre_req and post_req in
* order to support double buffering of requests (prepare one * order to support double buffering of requests (prepare one
* request while another request is active). * request while another request is active).
* pre_req() must always be followed by a post_req().
* To undo a call made to pre_req(), call post_req() with
* a nonzero err condition.
*/ */
void (*post_req)(struct mmc_host *host, struct mmc_request *req, void (*post_req)(struct mmc_host *host, struct mmc_request *req,
int err); int err);
@ -147,6 +151,7 @@ struct mmc_host_ops {
int (*execute_tuning)(struct mmc_host *host); int (*execute_tuning)(struct mmc_host *host);
void (*enable_preset_value)(struct mmc_host *host, bool enable); void (*enable_preset_value)(struct mmc_host *host, bool enable);
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
void (*hw_reset)(struct mmc_host *host);
}; };
struct mmc_card; struct mmc_card;
@ -229,8 +234,20 @@ struct mmc_host {
#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */ #define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */ #define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ #define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
unsigned int caps2; /* More host capabilities */
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
mmc_pm_flag_t pm_caps; /* supported pm features */ mmc_pm_flag_t pm_caps; /* supported pm features */
unsigned int power_notify_type;
#define MMC_HOST_PW_NOTIFY_NONE 0
#define MMC_HOST_PW_NOTIFY_SHORT 1
#define MMC_HOST_PW_NOTIFY_LONG 2
#ifdef CONFIG_MMC_CLKGATE #ifdef CONFIG_MMC_CLKGATE
int clk_requests; /* internal reference counter */ int clk_requests; /* internal reference counter */
@ -302,6 +319,10 @@ struct mmc_host {
struct mmc_async_req *areq; /* active async req */ struct mmc_async_req *areq; /* active async req */
#ifdef CONFIG_FAIL_MMC_REQUEST
struct fault_attr fail_mmc_request;
#endif
unsigned long private[0] ____cacheline_aligned; unsigned long private[0] ____cacheline_aligned;
}; };
@ -330,6 +351,8 @@ extern int mmc_power_restore_host(struct mmc_host *host);
extern void mmc_detect_change(struct mmc_host *, unsigned long delay); extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
extern void mmc_request_done(struct mmc_host *, struct mmc_request *); extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
extern int mmc_cache_ctrl(struct mmc_host *, u8);
static inline void mmc_signal_sdio_irq(struct mmc_host *host) static inline void mmc_signal_sdio_irq(struct mmc_host *host)
{ {
host->ops->enable_sdio_irq(host, 0); host->ops->enable_sdio_irq(host, 0);
@ -394,4 +417,10 @@ static inline int mmc_host_cmd23(struct mmc_host *host)
{ {
return host->caps & MMC_CAP_CMD23; return host->caps & MMC_CAP_CMD23;
} }
static inline int mmc_boot_partition_access(struct mmc_host *host)
{
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
}
#endif /* LINUX_MMC_HOST_H */ #endif /* LINUX_MMC_HOST_H */

View File

@ -270,18 +270,31 @@ struct _mmc_csd {
* EXT_CSD fields * EXT_CSD fields
*/ */
#define EXT_CSD_FLUSH_CACHE 32 /* W */
#define EXT_CSD_CACHE_CTRL 33 /* R/W */
#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
#define EXT_CSD_HPI_MGMT 161 /* R/W */
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
#define EXT_CSD_SANITIZE_START 165 /* W */
#define EXT_CSD_WR_REL_PARAM 166 /* RO */ #define EXT_CSD_WR_REL_PARAM 166 /* RO */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_PART_CONFIG 179 /* R/W */ #define EXT_CSD_PART_CONFIG 179 /* R/W */
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
#define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_POWER_CLASS 187 /* R/W */
#define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_STRUCTURE 194 /* RO */ #define EXT_CSD_STRUCTURE 194 /* RO */
#define EXT_CSD_CARD_TYPE 196 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ #define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
#define EXT_CSD_PWR_CL_26_195 201 /* RO */
#define EXT_CSD_PWR_CL_52_360 202 /* RO */
#define EXT_CSD_PWR_CL_26_360 203 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
#define EXT_CSD_S_A_TIMEOUT 217 /* RO */ #define EXT_CSD_S_A_TIMEOUT 217 /* RO */
#define EXT_CSD_REL_WR_SEC_C 222 /* RO */ #define EXT_CSD_REL_WR_SEC_C 222 /* RO */
@ -293,6 +306,14 @@ struct _mmc_csd {
#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */
#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
#define EXT_CSD_TRIM_MULT 232 /* RO */ #define EXT_CSD_TRIM_MULT 232 /* RO */
#define EXT_CSD_PWR_CL_200_195 236 /* RO */
#define EXT_CSD_PWR_CL_200_360 237 /* RO */
#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */
#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_HPI_FEATURES 503 /* RO */
/* /*
* EXT_CSD field definitions * EXT_CSD field definitions
@ -302,7 +323,9 @@ struct _mmc_csd {
#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) #define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x2) #define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4)
#define EXT_CSD_PART_SUPPORT_PART_EN (0x1)
#define EXT_CSD_CMD_SET_NORMAL (1<<0) #define EXT_CSD_CMD_SET_NORMAL (1<<0)
#define EXT_CSD_CMD_SET_SECURE (1<<1) #define EXT_CSD_CMD_SET_SECURE (1<<1)
@ -327,7 +350,20 @@ struct _mmc_csd {
#define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_ER_EN BIT(0)
#define EXT_CSD_SEC_BD_BLK_EN BIT(2) #define EXT_CSD_SEC_BD_BLK_EN BIT(2)
#define EXT_CSD_SEC_GB_CL_EN BIT(4) #define EXT_CSD_SEC_GB_CL_EN BIT(4)
#define EXT_CSD_SEC_SANITIZE BIT(6) /* v4.5 only */
#define EXT_CSD_RST_N_EN_MASK 0x3
#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */
#define EXT_CSD_NO_POWER_NOTIFICATION 0
#define EXT_CSD_POWER_ON 1
#define EXT_CSD_POWER_OFF_SHORT 2
#define EXT_CSD_POWER_OFF_LONG 3
#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */
#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */
#define EXT_CSD_PWR_CL_8BIT_SHIFT 4
#define EXT_CSD_PWR_CL_4BIT_SHIFT 0
/* /*
* MMC_SWITCH access modes * MMC_SWITCH access modes
*/ */

View File

@ -88,6 +88,10 @@ struct sdhci_host {
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */ /* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31) #define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
unsigned int quirks2; /* More deviations from spec. */
#define SDHCI_QUIRK2_OWN_CARD_DETECTION (1<<0)
int irq; /* Device IRQ */ int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */ void __iomem *ioaddr; /* Mapped address */
@ -115,6 +119,8 @@ struct sdhci_host {
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */ #define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ #define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
unsigned int version; /* SDHCI spec. version */ unsigned int version; /* SDHCI spec. version */
@ -125,6 +131,8 @@ struct sdhci_host {
unsigned int clock; /* Current clock (MHz) */ unsigned int clock; /* Current clock (MHz) */
u8 pwr; /* Current voltage */ u8 pwr; /* Current voltage */
bool runtime_suspended; /* Host is runtime suspended */
struct mmc_request *mrq; /* Current request */ struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */ struct mmc_command *cmd; /* Current command */
struct mmc_data *data; /* Current data request */ struct mmc_data *data; /* Current data request */

View File

@ -72,11 +72,13 @@
#define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */ #define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */
#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */ #define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */
#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */ #define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */
#define SDIO_CCCR_REV_3_00 3 /* CCCR/FBR Version 3.00 */
#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */ #define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */
#define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */ #define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */
#define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */ #define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */
#define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */ #define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */
#define SDIO_SDIO_REV_3_00 4 /* SDIO Spec Version 3.00 */
#define SDIO_CCCR_SD 0x01 #define SDIO_CCCR_SD 0x01

View File

@ -41,7 +41,9 @@ struct sh_mmcif_plat_data {
void (*set_pwr)(struct platform_device *pdev, int state); void (*set_pwr)(struct platform_device *pdev, int state);
void (*down_pwr)(struct platform_device *pdev); void (*down_pwr)(struct platform_device *pdev);
int (*get_cd)(struct platform_device *pdef); int (*get_cd)(struct platform_device *pdef);
struct sh_mmcif_dma *dma; struct sh_mmcif_dma *dma; /* Deprecated. Instead */
unsigned int slave_id_tx; /* use embedded slave_id_[tr]x */
unsigned int slave_id_rx;
u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
unsigned long caps; unsigned long caps;
u32 ocr; u32 ocr;

View File

@ -6,6 +6,10 @@
struct platform_device; struct platform_device;
struct tmio_mmc_data; struct tmio_mmc_data;
#define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect"
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
struct sh_mobile_sdhi_info { struct sh_mobile_sdhi_info {
int dma_slave_tx; int dma_slave_tx;
int dma_slave_rx; int dma_slave_rx;

View File

@ -1070,6 +1070,17 @@ config FAIL_IO_TIMEOUT
Only works with drivers that use the generic timeout handling, Only works with drivers that use the generic timeout handling,
for others it wont do anything. for others it wont do anything.
config FAIL_MMC_REQUEST
bool "Fault-injection capability for MMC IO"
select DEBUG_FS
depends on FAULT_INJECTION && MMC
help
Provide fault-injection capability for MMC IO.
This will make the mmc core return data errors. This is
useful to test the error handling in the mmc block device
and to test how the mmc host driver handles retries from
the block device.
config FAULT_INJECTION_DEBUG_FS config FAULT_INJECTION_DEBUG_FS
bool "Debugfs entries for fault-injection capabilities" bool "Debugfs entries for fault-injection capabilities"
depends on FAULT_INJECTION && SYSFS && DEBUG_FS depends on FAULT_INJECTION && SYSFS && DEBUG_FS

View File

@ -14,7 +14,7 @@
* setup_fault_attr() is a helper function for various __setup handlers, so it * setup_fault_attr() is a helper function for various __setup handlers, so it
* returns 0 on error, because that is what __setup handlers do. * returns 0 on error, because that is what __setup handlers do.
*/ */
int __init setup_fault_attr(struct fault_attr *attr, char *str) int setup_fault_attr(struct fault_attr *attr, char *str)
{ {
unsigned long probability; unsigned long probability;
unsigned long interval; unsigned long interval;
@ -36,6 +36,7 @@ int __init setup_fault_attr(struct fault_attr *attr, char *str)
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(setup_fault_attr);
static void fail_dump(struct fault_attr *attr) static void fail_dump(struct fault_attr *attr)
{ {
@ -130,6 +131,7 @@ bool should_fail(struct fault_attr *attr, ssize_t size)
return true; return true;
} }
EXPORT_SYMBOL_GPL(should_fail);
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
@ -243,5 +245,6 @@ fail:
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
EXPORT_SYMBOL_GPL(fault_create_debugfs_attr);
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */