mmc: core: support HPI send command
HPI command is defined in eMMC4.41. This feature is important for eMMC4.5 devices. Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
		
							parent
							
								
									881d1c25f7
								
							
						
					
					
						commit
						eb0d8f135b
					
				| @ -379,6 +379,63 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) | ||||
| } | ||||
| 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 | ||||
|  *	@host: MMC host to start command | ||||
|  | ||||
| @ -448,6 +448,21 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | ||||
| 	} | ||||
| 
 | ||||
| 	if (card->ext_csd.rev >= 5) { | ||||
| 		/* 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]; | ||||
| 	} | ||||
| @ -895,6 +910,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. | ||||
| 	 */ | ||||
|  | ||||
| @ -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); | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| @ -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_card_sleepawake(struct mmc_host *host, int sleep); | ||||
| int mmc_bus_test(struct mmc_card *card, u8 bus_width); | ||||
| int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -69,10 +69,14 @@ struct mmc_ext_csd { | ||||
| 	unsigned long long	enhanced_area_offset;	/* Units: Byte */ | ||||
| 	unsigned int		enhanced_area_size;	/* Units: KB */ | ||||
| 	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_erased_mem_count;	/* 181 */ | ||||
| 	u8			raw_ext_csd_structure;	/* 194 */ | ||||
| 	u8			raw_card_type;		/* 196 */ | ||||
| 	u8			out_of_int_time;	/* 198 */ | ||||
| 	u8			raw_s_a_timeout;		/* 217 */ | ||||
| 	u8			raw_hc_erase_gap_size;	/* 221 */ | ||||
| 	u8			raw_erase_timeout_mult;	/* 223 */ | ||||
|  | ||||
| @ -136,6 +136,7 @@ struct mmc_async_req; | ||||
| 
 | ||||
| extern struct mmc_async_req *mmc_start_req(struct mmc_host *, | ||||
| 					   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 int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); | ||||
| extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); | ||||
|  | ||||
| @ -276,6 +276,7 @@ struct _mmc_csd { | ||||
| #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */ | ||||
| #define EXT_CSD_PARTITION_ATTRIBUTE	156	/* R/W */ | ||||
| #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 */ | ||||
| @ -288,6 +289,7 @@ struct _mmc_csd { | ||||
| #define EXT_CSD_REV			192	/* RO */ | ||||
| #define EXT_CSD_STRUCTURE		194	/* 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_PWR_CL_52_195		200	/* RO */ | ||||
| #define EXT_CSD_PWR_CL_26_195		201	/* RO */ | ||||
| @ -311,6 +313,7 @@ struct _mmc_csd { | ||||
| #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 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user