Commit 08090950 authored by Linus Torvalds's avatar Linus Torvalds

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
parents bc6c8364 75b53aee
...@@ -1411,7 +1411,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -1411,7 +1411,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
/* complete ongoing async transfer before issuing discard */ /* complete ongoing async transfer before issuing discard */
if (card->host->areq) if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL); 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); ret = mmc_blk_issue_secdiscard_rq(mq, req);
else else
ret = mmc_blk_issue_discard_rq(mq, req); ret = mmc_blk_issue_discard_rq(mq, req);
...@@ -1716,6 +1717,7 @@ static int mmc_add_disk(struct mmc_blk_data *md) ...@@ -1716,6 +1717,7 @@ static int mmc_add_disk(struct mmc_blk_data *md)
#define CID_MANFID_SANDISK 0x2 #define CID_MANFID_SANDISK 0x2
#define CID_MANFID_TOSHIBA 0x11 #define CID_MANFID_TOSHIBA 0x11
#define CID_MANFID_MICRON 0x13 #define CID_MANFID_MICRON 0x13
#define CID_MANFID_SAMSUNG 0x15
static const struct mmc_fixup blk_fixups[] = static const struct mmc_fixup blk_fixups[] =
{ {
...@@ -1752,6 +1754,28 @@ 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_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
MMC_QUIRK_LONG_READ_TIME), 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 END_FIXUP
}; };
......
...@@ -81,6 +81,7 @@ struct atmel_mci_caps { ...@@ -81,6 +81,7 @@ struct atmel_mci_caps {
bool has_bad_data_ordering; bool has_bad_data_ordering;
bool need_reset_after_xfer; bool need_reset_after_xfer;
bool need_blksz_mul_4; bool need_blksz_mul_4;
bool need_notbusy_for_read_ops;
}; };
struct atmel_mci_dma { struct atmel_mci_dma {
...@@ -1625,7 +1626,8 @@ static void atmci_tasklet_func(unsigned long priv) ...@@ -1625,7 +1626,8 @@ static void atmci_tasklet_func(unsigned long priv)
__func__); __func__);
atmci_set_completed(host, EVENT_XFER_COMPLETE); 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); atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY; state = STATE_WAITING_NOTBUSY;
} else if (host->mrq->stop) { } else if (host->mrq->stop) {
...@@ -2218,6 +2220,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) ...@@ -2218,6 +2220,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_bad_data_ordering = 1; host->caps.has_bad_data_ordering = 1;
host->caps.need_reset_after_xfer = 1; host->caps.need_reset_after_xfer = 1;
host->caps.need_blksz_mul_4 = 1; host->caps.need_blksz_mul_4 = 1;
host->caps.need_notbusy_for_read_ops = 0;
/* keep only major version number */ /* keep only major version number */
switch (version & 0xf00) { switch (version & 0xf00) {
...@@ -2238,6 +2241,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) ...@@ -2238,6 +2241,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
case 0x200: case 0x200:
host->caps.has_rwproof = 1; host->caps.has_rwproof = 1;
host->caps.need_blksz_mul_4 = 0; host->caps.need_blksz_mul_4 = 0;
host->caps.need_notbusy_for_read_ops = 1;
case 0x100: case 0x100:
host->caps.has_bad_data_ordering = 0; host->caps.has_bad_data_ordering = 0;
host->caps.need_reset_after_xfer = 0; host->caps.need_reset_after_xfer = 0;
......
...@@ -49,13 +49,6 @@ ...@@ -49,13 +49,6 @@
#define bfin_write_SDH_CFG bfin_write_RSI_CFG #define bfin_write_SDH_CFG bfin_write_RSI_CFG
#endif #endif
struct dma_desc_array {
unsigned long start_addr;
unsigned short cfg;
unsigned short x_count;
short x_modify;
} __packed;
struct sdh_host { struct sdh_host {
struct mmc_host *mmc; struct mmc_host *mmc;
spinlock_t lock; spinlock_t lock;
......
...@@ -627,6 +627,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) ...@@ -627,6 +627,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
{ {
struct dw_mci *host = slot->host; struct dw_mci *host = slot->host;
u32 div; u32 div;
u32 clk_en_a;
if (slot->clock != host->current_speed) { if (slot->clock != host->current_speed) {
div = host->bus_hz / slot->clock; div = host->bus_hz / slot->clock;
...@@ -659,9 +660,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) ...@@ -659,9 +660,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
mci_send_cmd(slot, mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock */ /* enable clock; only low power if no SDIO */
mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE | clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
SDMMC_CLKEN_LOW_PWR) << 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 */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot,
...@@ -862,6 +865,30 @@ static int dw_mci_get_cd(struct mmc_host *mmc) ...@@ -862,6 +865,30 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
return present; 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) static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
{ {
struct dw_mci_slot *slot = mmc_priv(mmc); 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) ...@@ -871,6 +898,14 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
/* Enable/disable Slot Specific SDIO interrupt */ /* Enable/disable Slot Specific SDIO interrupt */
int_mask = mci_readl(host, INTMASK); int_mask = mci_readl(host, INTMASK);
if (enb) { 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, mci_writel(host, INTMASK,
(int_mask | SDMMC_INT_SDIO(slot->id))); (int_mask | SDMMC_INT_SDIO(slot->id)));
} else { } else {
...@@ -1429,22 +1464,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host) ...@@ -1429,22 +1464,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
nbytes += len; nbytes += len;
remain -= len; remain -= len;
} while (remain); } while (remain);
sg_miter->consumed = offset;
sg_miter->consumed = offset;
status = mci_readl(host, MINTSTS); status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_RXDR); 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*/ } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
data->bytes_xfered += nbytes; data->bytes_xfered += nbytes;
...@@ -1497,23 +1520,10 @@ static void dw_mci_write_data_pio(struct dw_mci *host) ...@@ -1497,23 +1520,10 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
nbytes += len; nbytes += len;
remain -= len; remain -= len;
} while (remain); } while (remain);
sg_miter->consumed = offset;
sg_miter->consumed = offset;
status = mci_readl(host, MINTSTS); status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_TXDR); 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 */ } while (status & SDMMC_INT_TXDR); /* if TXDR write again */
data->bytes_xfered += nbytes; data->bytes_xfered += nbytes;
...@@ -1547,12 +1557,11 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) ...@@ -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) 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 pending;
unsigned int pass_count = 0; unsigned int pass_count = 0;
int i; int i;
do { do {
status = mci_readl(host, RINTSTS);
pending = mci_readl(host, MINTSTS); /* read-only mask reg */ pending = mci_readl(host, MINTSTS); /* read-only mask reg */
/* /*
...@@ -1570,7 +1579,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) ...@@ -1570,7 +1579,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & DW_MCI_CMD_ERROR_FLAGS) { if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = status; host->cmd_status = pending;
smp_wmb(); smp_wmb();
set_bit(EVENT_CMD_COMPLETE, &host->pending_events); set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
} }
...@@ -1578,18 +1587,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) ...@@ -1578,18 +1587,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & DW_MCI_DATA_ERROR_FLAGS) { if (pending & DW_MCI_DATA_ERROR_FLAGS) {
/* if there is an error report DATA_ERROR */ /* if there is an error report DATA_ERROR */
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
host->data_status = status; host->data_status = pending;
smp_wmb(); smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events); 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) { if (pending & SDMMC_INT_DATA_OVER) {
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status) if (!host->data_status)
host->data_status = status; host->data_status = pending;
smp_wmb(); smp_wmb();
if (host->dir_status == DW_MCI_RECV_STATUS) { if (host->dir_status == DW_MCI_RECV_STATUS) {
if (host->sg != NULL) if (host->sg != NULL)
...@@ -1613,7 +1620,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) ...@@ -1613,7 +1620,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_CMD_DONE) { if (pending & SDMMC_INT_CMD_DONE) {
mci_writel(host, RINTSTS, 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) { if (pending & SDMMC_INT_CD) {
......
...@@ -285,11 +285,11 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) ...@@ -285,11 +285,11 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
writel(stat & MXS_MMC_IRQ_BITS, writel(stat & MXS_MMC_IRQ_BITS,
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); 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)) if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
mmc_signal_sdio_irq(host->mmc); mmc_signal_sdio_irq(host->mmc);
spin_unlock(&host->lock);
if (stat & BM_SSP_CTRL1_RESP_TIMEOUT_IRQ) if (stat & BM_SSP_CTRL1_RESP_TIMEOUT_IRQ)
cmd->error = -ETIMEDOUT; cmd->error = -ETIMEDOUT;
else if (stat & BM_SSP_CTRL1_RESP_ERR_IRQ) 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) ...@@ -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); host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
writel(BM_SSP_CTRL1_SDIO_IRQ_EN, writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET); 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 { } else {
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); 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) ...@@ -657,6 +652,11 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
} }
spin_unlock_irqrestore(&host->lock, flags); 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 = { static const struct mmc_host_ops mxs_mmc_ops = {
......
...@@ -668,7 +668,7 @@ mmc_omap_clk_timer(unsigned long data) ...@@ -668,7 +668,7 @@ mmc_omap_clk_timer(unsigned long data)
static void static void
mmc_omap_xfer_data(struct mmc_omap_host *host, int write) mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
{ {
int n; int n, nwords;
if (host->buffer_bytes_left == 0) { if (host->buffer_bytes_left == 0) {
host->sg_idx++; host->sg_idx++;
...@@ -678,15 +678,23 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write) ...@@ -678,15 +678,23 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
n = 64; n = 64;
if (n > host->buffer_bytes_left) if (n > host->buffer_bytes_left)
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->buffer_bytes_left -= n;
host->total_bytes_left -= n; host->total_bytes_left -= n;
host->data->bytes_xfered += n; host->data->bytes_xfered += n;
if (write) { 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 { } 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) static inline void mmc_omap_report_irq(u16 status)
......
...@@ -48,14 +48,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -48,14 +48,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
int div = 1; int div = 1;
u32 temp; u32 temp;
if (clock == 0)
goto out;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK); | ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
if (clock == 0)
goto out;
while (host->max_clk / pre_div / 16 > clock && pre_div < 256) while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2; pre_div *= 2;
......
...@@ -239,6 +239,7 @@ struct mmc_card { ...@@ -239,6 +239,7 @@ struct mmc_card {
#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 */ #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_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 */ /* byte mode */
unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */ unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION 0 #define MMC_NO_POWER_NOTIFICATION 0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment