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

Commit 7ccd266e authored by Pierre Ossman's avatar Pierre Ossman
Browse files

mmc: Support for high speed SD cards



Modern SD cards support a clock speed of 50 MHz. Make sure we test for
this capability and do the song and dance required to activate it.

Activating high speed support actually modifies the TRAN_SPEED field
of the CSD. But as the spec says that the cards must report 50 MHz,
we might as well skip re-reading the CSD.

Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 73778120
Loading
Loading
Loading
Loading
+118 −4
Original line number Diff line number Diff line
@@ -1157,6 +1157,116 @@ static void mmc_read_scrs(struct mmc_host *host)
	mmc_deselect_cards(host);
}

static void mmc_read_switch_caps(struct mmc_host *host)
{
	int err;
	struct mmc_card *card;
	struct mmc_request mrq;
	struct mmc_command cmd;
	struct mmc_data data;
	unsigned char *status;
	struct scatterlist sg;

	status = kmalloc(64, GFP_KERNEL);
	if (!status) {
		printk(KERN_WARNING "%s: Unable to allocate buffer for "
			"reading switch capabilities.\n",
			mmc_hostname(host));
		return;
	}

	list_for_each_entry(card, &host->cards, node) {
		if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
			continue;
		if (!mmc_card_sd(card))
			continue;
		if (card->scr.sda_vsn < SCR_SPEC_VER_1)
			continue;

		err = mmc_select_card(host, card);
		if (err != MMC_ERR_NONE) {
			mmc_card_set_dead(card);
			continue;
		}

		memset(&cmd, 0, sizeof(struct mmc_command));

		cmd.opcode = SD_SWITCH;
		cmd.arg = 0x00FFFFF1;
		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

		memset(&data, 0, sizeof(struct mmc_data));

		mmc_set_data_timeout(&data, card, 0);

		data.blksz = 64;
		data.blocks = 1;
		data.flags = MMC_DATA_READ;
		data.sg = &sg;
		data.sg_len = 1;

		memset(&mrq, 0, sizeof(struct mmc_request));

		mrq.cmd = &cmd;
		mrq.data = &data;

		sg_init_one(&sg, status, 64);

		mmc_wait_for_req(host, &mrq);

		if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
			mmc_card_set_dead(card);
			continue;
		}

		if (status[13] & 0x02)
			card->sw_caps.hs_max_dtr = 50000000;

		memset(&cmd, 0, sizeof(struct mmc_command));

		cmd.opcode = SD_SWITCH;
		cmd.arg = 0x80FFFFF1;
		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

		memset(&data, 0, sizeof(struct mmc_data));

		mmc_set_data_timeout(&data, card, 0);

		data.blksz = 64;
		data.blocks = 1;
		data.flags = MMC_DATA_READ;
		data.sg = &sg;
		data.sg_len = 1;

		memset(&mrq, 0, sizeof(struct mmc_request));

		mrq.cmd = &cmd;
		mrq.data = &data;

		sg_init_one(&sg, status, 64);

		mmc_wait_for_req(host, &mrq);

		if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
			mmc_card_set_dead(card);
			continue;
		}

		if ((status[16] & 0xF) != 1) {
			printk(KERN_WARNING "%s: Problem switching card "
				"into high-speed mode!\n",
				mmc_hostname(host));
			continue;
		}

		mmc_card_set_highspeed(card);
	}

	kfree(status);

	mmc_deselect_cards(host);
}

static unsigned int mmc_calculate_clock(struct mmc_host *host)
{
	struct mmc_card *card;
@@ -1164,7 +1274,10 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host)

	list_for_each_entry(card, &host->cards, node)
		if (!mmc_card_dead(card)) {
			if (mmc_card_highspeed(card)) {
			if (mmc_card_highspeed(card) && mmc_card_sd(card)) {
				if (max_dtr > card->sw_caps.hs_max_dtr)
					max_dtr = card->sw_caps.hs_max_dtr;
			} else if (mmc_card_highspeed(card) && !mmc_card_sd(card)) {
				if (max_dtr > card->ext_csd.hs_max_dtr)
					max_dtr = card->ext_csd.hs_max_dtr;
			} else if (max_dtr > card->csd.max_dtr) {
@@ -1288,9 +1401,10 @@ static void mmc_setup(struct mmc_host *host)

	mmc_read_csds(host);

	if (host->mode == MMC_MODE_SD)
	if (host->mode == MMC_MODE_SD) {
		mmc_read_scrs(host);
	else
		mmc_read_switch_caps(host);
	} else
		mmc_process_ext_csds(host);
}

+6 −1
Original line number Diff line number Diff line
@@ -50,6 +50,10 @@ struct sd_scr {
#define SD_SCR_BUS_WIDTH_4	(1<<2)
};

struct sd_switch_caps {
	unsigned int		hs_max_dtr;
};

struct mmc_host;

/*
@@ -66,7 +70,7 @@ struct mmc_card {
#define MMC_STATE_BAD		(1<<2)		/* unrecognised device */
#define MMC_STATE_SDCARD	(1<<3)		/* is an SD card */
#define MMC_STATE_READONLY	(1<<4)		/* card is read-only */
#define MMC_STATE_HIGHSPEED	(1<<5)		/* card is in mmc4 highspeed mode */
#define MMC_STATE_HIGHSPEED	(1<<5)		/* card is in high speed mode */
	u32			raw_cid[4];	/* raw card CID */
	u32			raw_csd[4];	/* raw card CSD */
	u32			raw_scr[2];	/* raw card SCR */
@@ -74,6 +78,7 @@ struct mmc_card {
	struct mmc_csd		csd;		/* card specific */
	struct mmc_ext_csd	ext_csd;	/* mmc v4 extended card specific */
	struct sd_scr		scr;		/* extra SD information */
	struct sd_switch_caps	sw_caps;	/* switch (CMD6) caps */
};

#define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
+22 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@
  /* class 8 */
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6  */
#define SD_SWITCH                 6   /* adtc [31:0] See below   R1  */

  /* Application commands */
#define SD_APP_SET_BUS_WIDTH      6   /* ac   [1:0] bus width    R1  */
@@ -100,6 +101,19 @@
 *	[02:00] Command Set
 */

/*
 * SD_SWITCH argument format:
 *
 *      [31] Check (0) or switch (1)
 *      [30:24] Reserved (0)
 *      [23:20] Function group 6
 *      [19:16] Function group 5
 *      [15:12] Function group 4
 *      [11:8] Function group 3
 *      [7:4] Function group 2
 *      [3:0] Function group 1
 */

/*
  MMC status in R1
  Type
@@ -284,6 +298,14 @@ struct _mmc_csd {
#define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1 in value */
#define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */

/*
 * SCR field definitions
 */

#define SCR_SPEC_VER_0      0           /* Implements system specification 1.0 - 1.01 */
#define SCR_SPEC_VER_1      1           /* Implements system specification 1.10 */
#define SCR_SPEC_VER_2      2           /* Implements system specification 2.00 */

/*
 * SD bus widths
 */