diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/card/block.c | 26 | ||||
| -rw-r--r-- | drivers/mmc/host/atmel-mci.c | 6 | ||||
| -rw-r--r-- | drivers/mmc/host/bfin_sdh.c | 7 | ||||
| -rw-r--r-- | drivers/mmc/host/dw_mmc.c | 85 | ||||
| -rw-r--r-- | drivers/mmc/host/mxs-mmc.c | 14 | ||||
| -rw-r--r-- | drivers/mmc/host/omap.c | 14 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci-esdhc.h | 6 | 
7 files changed, 97 insertions, 61 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index f1c84decb19..172a768036d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -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 @@ force_ro_fail:  #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  }; diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 322412cec4e..a53c7c478e0 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -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; diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 03666174ca4..a17dd7363ce 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -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; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 72dc3cde646..af40d227bec 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -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) { diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index a51f9309ffb..ad3fcea1269 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -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 = { diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 50e08f03aa6..a5999a74496 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -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) diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index b97b2f5dafd..d25f9ab9a54 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -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;  |