forked from mirrors/linux
		
	mmc: core: Add cache control for eMMC4.5 device
This patch adds cache feature of eMMC4.5 Spec. If device supports cache capability, host can utilize some specific operations. Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
		
							parent
							
								
									71fe3eb0d0
								
							
						
					
					
						commit
						881d1c25f7
					
				
					 7 changed files with 104 additions and 6 deletions
				
			
		| 
						 | 
					@ -851,16 +851,18 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
 | 
				
			||||||
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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2158,6 +2158,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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -2172,6 +2231,9 @@ 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) {
 | 
				
			||||||
| 
						 | 
					@ -2197,6 +2259,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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -470,6 +470,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		card->ext_csd.generic_cmd6_time = 0;
 | 
							card->ext_csd.generic_cmd6_time = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1020,6 +1026,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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@ 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;
 | 
						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 */
 | 
				
			||||||
| 
						 | 
					@ -67,6 +68,7 @@ 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		cache_size;		/* Units: KB */
 | 
				
			||||||
	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 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -177,6 +177,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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -239,6 +239,7 @@ struct mmc_host {
 | 
				
			||||||
	unsigned int		caps2;		/* More host capabilities */
 | 
						unsigned int		caps2;		/* More host capabilities */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MMC_CAP2_BOOTPART_NOACC	(1 << 0)	/* Boot partition no access */
 | 
					#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_POWEROFF_NOTIFY (1 << 2)	/* Notify poweroff supported */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 | 
						mmc_pm_flag_t		pm_caps;	/* supported pm features */
 | 
				
			||||||
| 
						 | 
					@ -349,6 +350,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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,6 +270,8 @@ 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_POWER_OFF_NOTIFICATION	34	/* R/W */
 | 
				
			||||||
#define EXT_CSD_GP_SIZE_MULT		143	/* 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 */
 | 
				
			||||||
| 
						 | 
					@ -308,6 +310,7 @@ struct _mmc_csd {
 | 
				
			||||||
#define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
 | 
					#define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
 | 
				
			||||||
#define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
 | 
					#define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
 | 
				
			||||||
#define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
 | 
					#define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
 | 
				
			||||||
 | 
					#define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * EXT_CSD field definitions
 | 
					 * EXT_CSD field definitions
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue