MMC fixes for 3.6-rc5:

- a firmware bug on several Samsung MoviNAND eMMC models causes
    permanent corruption on the device when secure erase and secure trim
    requests are made, so we disable those requests on these eMMC devices.
  - atmel-mci: fix a hang with some SD cards by waiting for not-busy flag.
  - dw_mmc: low-power mode breaks SDIO interrupts; fix PIO error handling;
    fix handling of error interrupts.
  - mxs-mmc: fix deadlocks; fix compile error due to dma.h arch change.
  - omap: fix broken PIO mode causing memory corruption.
  - sdhci-esdhc: fix card detection.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJQR/7BAAoJEHNBYZ7TNxYMT3gQANQDdD/wvYFgUvssIAgdQJdl
 KORu7ke5ks2Gvx0Ef0Ch+GXIFFDoqH0zMkQMXc+c0BdhR4BF/vNBbBYZoVYyvmgK
 GUHK740nznwp9edIVbGbRm+FSxDG7ZpjUlFq+SInBOehIw7tQAep3Tbv1rYZzu6M
 SUBGc31Nif9eCvTKzGU195qbutAhvcGmqEvi/ALP9bUCYR7QaTt/oD3YYuUD/ZjV
 O/mf3R30vrV+R1lrQRqzXIi8vCojPMzkVU8x+C8PdOTjewvYviM0huW9+Lv6WsxK
 DzhCgoNvrB1Q/rYtiNZ1gyWd5cTZWns308slREEwywD5IBQJMo5T68Q/D+h2PkS9
 JCvtiZ+ryycvINyUn2JxEZuygseUiT/nMS0ijSidQY7vrZlNR8JrrK3qf0vRDkWo
 0mXhQG5DvxDN6Dx9K15OgTAuABek1CEdKFD0R/a3X41H/KUvKQwWPWmdTybBriAo
 8jkUl64YMpFt+LZqrmeQ1oOcwc8BvMvcM4dnMbkXrHvBb926fENBIMGNeI7O10o8
 597dEpLlKNZj8e8YxJjbqaCuFhOp/pHRQy+rxdjP28V4mfHASQGgsnFAO4qV7B24
 1wqtSu5ZCor4K616wfDvMYgL88pMg7fVEV3nN9Cc/WpsBaBPZv7vOQbp/f40rKWa
 GRb9+9NC0WU0CP28GlCq
 =n1hi
 -----END PGP SIGNATURE-----

Merge tag 'mmc-fixes-for-3.6-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC fixes from Chris Ball:
 - a firmware bug on several Samsung MoviNAND eMMC models causes
   permanent corruption on the device when secure erase and secure trim
   requests are made, so we disable those requests on these eMMC devices.
 - atmel-mci: fix a hang with some SD cards by waiting for not-busy flag.
 - dw_mmc: low-power mode breaks SDIO interrupts; fix PIO error handling;
   fix handling of error interrupts.
 - mxs-mmc: fix deadlocks; fix compile error due to dma.h arch change.
 - omap: fix broken PIO mode causing memory corruption.
 - sdhci-esdhc: fix card detection.

* tag 'mmc-fixes-for-3.6-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc:
  mmc: omap: fix broken PIO mode
  mmc: card: Skip secure erase on MoviNAND; causes unrecoverable corruption.
  mmc: dw_mmc: Disable low power mode if SDIO interrupts are used
  mmc: dw_mmc: fix error handling in PIO mode
  mmc: dw_mmc: correct mishandling error interrupt
  mmc: dw_mmc: amend using error interrupt status
  mmc: atmel-mci: not busy flag has also to be used for read operations
  mmc: sdhci-esdhc: break out early if clock is 0
  mmc: mxs-mmc: fix deadlock caused by recursion loop
  mmc: mxs-mmc: fix deadlock in SDIO IRQ case
  mmc: bfin_sdh: fix dma_desc_array build error
This commit is contained in:
Linus Torvalds 2012-09-05 19:41:58 -07:00
commit 0809095036
8 changed files with 98 additions and 61 deletions

View file

@ -1411,7 +1411,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
if (req->cmd_flags & REQ_SECURE)
if (req->cmd_flags & REQ_SECURE &&
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
ret = mmc_blk_issue_secdiscard_rq(mq, req);
else
ret = mmc_blk_issue_discard_rq(mq, req);
@ -1716,6 +1717,7 @@ static int mmc_add_disk(struct mmc_blk_data *md)
#define CID_MANFID_SANDISK 0x2
#define CID_MANFID_TOSHIBA 0x11
#define CID_MANFID_MICRON 0x13
#define CID_MANFID_SAMSUNG 0x15
static const struct mmc_fixup blk_fixups[] =
{
@ -1752,6 +1754,28 @@ static const struct mmc_fixup blk_fixups[] =
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
MMC_QUIRK_LONG_READ_TIME),
/*
* On these Samsung MoviNAND parts, performing secure erase or
* secure trim can result in unrecoverable corruption due to a
* firmware bug.
*/
MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
END_FIXUP
};

View file

@ -81,6 +81,7 @@ struct atmel_mci_caps {
bool has_bad_data_ordering;
bool need_reset_after_xfer;
bool need_blksz_mul_4;
bool need_notbusy_for_read_ops;
};
struct atmel_mci_dma {
@ -1625,7 +1626,8 @@ static void atmci_tasklet_func(unsigned long priv)
__func__);
atmci_set_completed(host, EVENT_XFER_COMPLETE);
if (host->data->flags & MMC_DATA_WRITE) {
if (host->caps.need_notbusy_for_read_ops ||
(host->data->flags & MMC_DATA_WRITE)) {
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY;
} else if (host->mrq->stop) {
@ -2218,6 +2220,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_bad_data_ordering = 1;
host->caps.need_reset_after_xfer = 1;
host->caps.need_blksz_mul_4 = 1;
host->caps.need_notbusy_for_read_ops = 0;
/* keep only major version number */
switch (version & 0xf00) {
@ -2238,6 +2241,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
case 0x200:
host->caps.has_rwproof = 1;
host->caps.need_blksz_mul_4 = 0;
host->caps.need_notbusy_for_read_ops = 1;
case 0x100:
host->caps.has_bad_data_ordering = 0;
host->caps.need_reset_after_xfer = 0;

View file

@ -49,13 +49,6 @@
#define bfin_write_SDH_CFG bfin_write_RSI_CFG
#endif
struct dma_desc_array {
unsigned long start_addr;
unsigned short cfg;
unsigned short x_count;
short x_modify;
} __packed;
struct sdh_host {
struct mmc_host *mmc;
spinlock_t lock;

View file

@ -627,6 +627,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
{
struct dw_mci *host = slot->host;
u32 div;
u32 clk_en_a;
if (slot->clock != host->current_speed) {
div = host->bus_hz / slot->clock;
@ -659,9 +660,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock */
mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE |
SDMMC_CLKEN_LOW_PWR) << slot->id));
/* enable clock; only low power if no SDIO */
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id)))
clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */
mci_send_cmd(slot,
@ -862,6 +865,30 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
return present;
}
/*
* Disable lower power mode.
*
* Low power mode will stop the card clock when idle. According to the
* description of the CLKENA register we should disable low power mode
* for SDIO cards if we need SDIO interrupts to work.
*
* This function is fast if low power mode is already disabled.
*/
static void dw_mci_disable_low_power(struct dw_mci_slot *slot)
{
struct dw_mci *host = slot->host;
u32 clk_en_a;
const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
clk_en_a = mci_readl(host, CLKENA);
if (clk_en_a & clken_low_pwr) {
mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
SDMMC_CMD_PRV_DAT_WAIT, 0);
}
}
static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
@ -871,6 +898,14 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
/* Enable/disable Slot Specific SDIO interrupt */
int_mask = mci_readl(host, INTMASK);
if (enb) {
/*
* Turn off low power mode if it was enabled. This is a bit of
* a heavy operation and we disable / enable IRQs a lot, so
* we'll leave low power mode disabled and it will get
* re-enabled again in dw_mci_setup_bus().
*/
dw_mci_disable_low_power(slot);
mci_writel(host, INTMASK,
(int_mask | SDMMC_INT_SDIO(slot->id)));
} else {
@ -1429,22 +1464,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
nbytes += len;
remain -= len;
} while (remain);
sg_miter->consumed = offset;
sg_miter->consumed = offset;
status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
if (status & DW_MCI_DATA_ERROR_FLAGS) {
host->data_status = status;
data->bytes_xfered += nbytes;
sg_miter_stop(sg_miter);
host->sg = NULL;
smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events);
tasklet_schedule(&host->tasklet);
return;
}
} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
data->bytes_xfered += nbytes;
@ -1497,23 +1520,10 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
nbytes += len;
remain -= len;
} while (remain);
sg_miter->consumed = offset;
sg_miter->consumed = offset;
status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
if (status & DW_MCI_DATA_ERROR_FLAGS) {
host->data_status = status;
data->bytes_xfered += nbytes;
sg_miter_stop(sg_miter);
host->sg = NULL;
smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events);
tasklet_schedule(&host->tasklet);
return;
}
} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
data->bytes_xfered += nbytes;
@ -1547,12 +1557,11 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
struct dw_mci *host = dev_id;
u32 status, pending;
u32 pending;
unsigned int pass_count = 0;
int i;
do {
status = mci_readl(host, RINTSTS);
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
/*
@ -1570,7 +1579,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = status;
host->cmd_status = pending;
smp_wmb();
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
}
@ -1578,18 +1587,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & DW_MCI_DATA_ERROR_FLAGS) {
/* if there is an error report DATA_ERROR */
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
host->data_status = status;
host->data_status = pending;
smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events);
if (!(pending & (SDMMC_INT_DTO | SDMMC_INT_DCRC |
SDMMC_INT_SBE | SDMMC_INT_EBE)))
tasklet_schedule(&host->tasklet);
tasklet_schedule(&host->tasklet);
}
if (pending & SDMMC_INT_DATA_OVER) {
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
host->data_status = status;
host->data_status = pending;
smp_wmb();
if (host->dir_status == DW_MCI_RECV_STATUS) {
if (host->sg != NULL)
@ -1613,7 +1620,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_CMD_DONE) {
mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
dw_mci_cmd_interrupt(host, status);
dw_mci_cmd_interrupt(host, pending);
}
if (pending & SDMMC_INT_CD) {

View file

@ -285,11 +285,11 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
writel(stat & MXS_MMC_IRQ_BITS,
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
spin_unlock(&host->lock);
if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
mmc_signal_sdio_irq(host->mmc);
spin_unlock(&host->lock);
if (stat & BM_SSP_CTRL1_RESP_TIMEOUT_IRQ)
cmd->error = -ETIMEDOUT;
else if (stat & BM_SSP_CTRL1_RESP_ERR_IRQ)
@ -644,11 +644,6 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
if (readl(host->base + HW_SSP_STATUS(host)) &
BM_SSP_STATUS_SDIO_IRQ)
mmc_signal_sdio_irq(host->mmc);
} else {
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
@ -657,6 +652,11 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
}
spin_unlock_irqrestore(&host->lock, flags);
if (enable && readl(host->base + HW_SSP_STATUS(host)) &
BM_SSP_STATUS_SDIO_IRQ)
mmc_signal_sdio_irq(host->mmc);
}
static const struct mmc_host_ops mxs_mmc_ops = {

View file

@ -668,7 +668,7 @@ mmc_omap_clk_timer(unsigned long data)
static void
mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
{
int n;
int n, nwords;
if (host->buffer_bytes_left == 0) {
host->sg_idx++;
@ -678,15 +678,23 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
n = 64;
if (n > host->buffer_bytes_left)
n = host->buffer_bytes_left;
nwords = n / 2;
nwords += n & 1; /* handle odd number of bytes to transfer */
host->buffer_bytes_left -= n;
host->total_bytes_left -= n;
host->data->bytes_xfered += n;
if (write) {
__raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
__raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA),
host->buffer, nwords);
} else {
__raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
__raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA),
host->buffer, nwords);
}
host->buffer += nwords;
}
static inline void mmc_omap_report_irq(u16 status)

View file

@ -48,14 +48,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
int div = 1;
u32 temp;
if (clock == 0)
goto out;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
if (clock == 0)
goto out;
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;

View file

@ -239,6 +239,7 @@ struct mmc_card {
#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 */
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
/* byte mode */
unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION 0