Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb0d8f13 authored by Jaehoon Chung's avatar Jaehoon Chung Committed by Chris Ball
Browse files

mmc: core: support HPI send command



HPI command is defined in eMMC4.41.
This feature is important for eMMC4.5 devices.

Signed-off-by: default avatarJaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 881d1c25
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -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
+31 −0
Original line number Diff line number Diff line
@@ -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.
	 */
+31 −0
Original line number Diff line number Diff line
@@ -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;
}
+1 −0
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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 */
Loading