svn commit: r183704 - head/sys/dev/mmc

Alexander Motin mav at FreeBSD.org
Wed Oct 8 17:35:42 UTC 2008


Author: mav
Date: Wed Oct  8 17:35:41 2008
New Revision: 183704
URL: http://svn.freebsd.org/changeset/base/183704

Log:
  Set of mmc layer improvements:
   - add MMC support.
   - add SDHC support.
   - add 4 and 8 bit bus width support.
   - add High Speed bus timing support.

Modified:
  head/sys/dev/mmc/bridge.h
  head/sys/dev/mmc/mmc.c
  head/sys/dev/mmc/mmcbrvar.h
  head/sys/dev/mmc/mmcreg.h
  head/sys/dev/mmc/mmcsd.c
  head/sys/dev/mmc/mmcvar.h

Modified: head/sys/dev/mmc/bridge.h
==============================================================================
--- head/sys/dev/mmc/bridge.h	Wed Oct  8 16:46:36 2008	(r183703)
+++ head/sys/dev/mmc/bridge.h	Wed Oct  8 17:35:41 2008	(r183704)
@@ -104,6 +104,10 @@ enum mmc_bus_width {
 	bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3
 };
 
+enum mmc_bus_timing {
+	bus_timing_normal = 0, bus_timing_hs
+};
+
 struct mmc_ios {
 	uint32_t	clock;	/* Speed of the clock in Hz to move data */
 	enum mmc_vdd	vdd;	/* Voltage to apply to the power pins/ */
@@ -111,6 +115,7 @@ struct mmc_ios {
 	enum mmc_chip_select chip_select;
 	enum mmc_bus_width bus_width;
 	enum mmc_power_mode power_mode;
+	enum mmc_bus_timing timing;
 };
 
 enum mmc_card_mode {
@@ -125,6 +130,7 @@ struct mmc_host {
 	uint32_t caps;
 #define MMC_CAP_4_BIT_DATA	(1 << 0) /* Can do 4-bit data transfers */
 #define MMC_CAP_8_BIT_DATA	(1 << 1) /* Can do 8-bit data transfers */
+#define MMC_CAP_HSPEED		(1 << 2) /* Can do High Speed transfers */
 	enum mmc_card_mode mode;
 	struct mmc_ios ios;	/* Current state of the host */
 };

Modified: head/sys/dev/mmc/mmc.c
==============================================================================
--- head/sys/dev/mmc/mmc.c	Wed Oct  8 16:46:36 2008	(r183703)
+++ head/sys/dev/mmc/mmc.c	Wed Oct  8 17:35:41 2008	(r183704)
@@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/module.h>
 #include <sys/mutex.h>
 #include <sys/bus.h>
+#include <sys/endian.h>
 
 #include <dev/mmc/mmcreg.h>
 #include <dev/mmc/mmcbrvar.h>
@@ -82,11 +83,19 @@ struct mmc_softc {
 struct mmc_ivars {
 	uint32_t raw_cid[4];	/* Raw bits of the CID */
 	uint32_t raw_csd[4];	/* Raw bits of the CSD */
+	uint32_t raw_scr[2];	/* Raw bits of the SCR */
+	uint8_t raw_ext_csd[512];	/* Raw bits of the EXT_CSD */
 	uint16_t rca;
 	enum mmc_card_mode mode;
 	struct mmc_cid cid;	/* cid decoded */
 	struct mmc_csd csd;	/* csd decoded */
+	struct mmc_scr scr;	/* scr decoded */
 	u_char read_only;	/* True when the device is read-only */
+	u_char bus_width;	/* Bus width to use */
+	u_char timing;		/* Bus timing support */
+	u_char high_cap;	/* High Capacity card */
+	uint32_t tran_speed;	/* Max speed in normal mode */
+	uint32_t hs_tran_speed;	/* Max speed in high speed mode */
 };
 
 #define CMD_RETRIES	3
@@ -111,6 +120,11 @@ static int mmc_wait_for_cmd(struct mmc_s
     int retries);
 static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode,
     uint32_t arg, uint32_t flags, uint32_t *resp, int retries);
+static int mmc_select_card(struct mmc_softc *sc, uint16_t rca);
+static int mmc_set_bus_width(struct mmc_softc *sc, uint16_t rca, int width);
+static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr);
+static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr);
+static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd);
 
 static void
 mmc_ms_delay(int ms)
@@ -173,6 +187,7 @@ static int
 mmc_acquire_bus(device_t busdev, device_t dev)
 {
 	struct mmc_softc *sc;
+	struct mmc_ivars *ivar;
 	int err;
 	int rca;
 
@@ -195,21 +210,26 @@ mmc_acquire_bus(device_t busdev, device_
 		 */
 		rca = mmc_get_rca(dev);
 		if (sc->last_rca != rca) {
-			mmc_wait_for_command(sc, MMC_SELECT_CARD, rca << 16,
-			    MMC_RSP_R1 | MMC_CMD_AC, NULL, CMD_RETRIES);
+			mmc_select_card(sc, rca);
 			sc->last_rca = rca;
+			/* Prepare bus width for the new card. */
+			ivar = device_get_ivars(dev);
+			device_printf(busdev,
+			    "setting bus width to %d bits\n",
+			    (ivar->bus_width == bus_width_4)?4:
+			    (ivar->bus_width == bus_width_8)?8:1);
+			mmc_set_bus_width(sc, rca, ivar->bus_width);
+			mmcbr_set_bus_width(busdev, ivar->bus_width);
+			mmcbr_update_ios(busdev);
 		}
-		/* XXX should set bus width here? */
 	} else {
 		/*
 		 * If there's a card selected, stand down.
 		 */
 		if (sc->last_rca != 0) {
-			mmc_wait_for_command(sc, MMC_SELECT_CARD, 0,
-			    MMC_RSP_R1 | MMC_CMD_AC, NULL, CMD_RETRIES);
+			mmc_select_card(sc, 0);
 			sc->last_rca = 0;
 		}
-		/* XXX should set bus width here? */
 	}
 
 	return (0);
@@ -444,6 +464,22 @@ mmc_send_op_cond(struct mmc_softc *sc, u
 	return (err);
 }
 
+static int
+mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.opcode = SD_SEND_IF_COND;
+	cmd.arg = (vhs << 8) + 0xAA;
+	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+	cmd.data = NULL;
+
+	err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+	return (err);
+}
+
 static void
 mmc_power_up(struct mmc_softc *sc)
 {
@@ -460,6 +496,7 @@ mmc_power_up(struct mmc_softc *sc)
 	mmc_ms_delay(1);
 
 	mmcbr_set_clock(dev, mmcbr_get_f_min(sc->dev));
+	mmcbr_set_timing(dev, bus_timing_normal);
 	mmcbr_set_power_mode(dev, power_on);
 	mmcbr_update_ios(dev);
 	mmc_ms_delay(2);
@@ -475,9 +512,207 @@ mmc_power_down(struct mmc_softc *sc)
 	mmcbr_set_bus_width(dev, bus_width_1);
 	mmcbr_set_power_mode(dev, power_off);
 	mmcbr_set_clock(dev, 0);
+	mmcbr_set_timing(dev, bus_timing_normal);
 	mmcbr_update_ios(dev);
 }
 
+static int
+mmc_select_card(struct mmc_softc *sc, uint16_t rca)
+{
+	return (mmc_wait_for_command(sc, MMC_SELECT_CARD, ((uint32_t)rca) << 16,
+	    MMC_RSP_R1B | MMC_CMD_AC, NULL, CMD_RETRIES));
+}
+
+static int
+mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value)
+{
+	struct mmc_command cmd;
+	int err;
+
+	cmd.opcode = MMC_SWITCH_FUNC;
+	cmd.arg = (MMC_SWITCH_FUNC_WR << 24) |
+	    (index << 16) |
+	    (value << 8) |
+	    set;
+	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+	cmd.data = NULL;
+	err = mmc_wait_for_cmd(sc, &cmd, 0);
+	return (err);
+}
+
+static int
+mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, uint8_t *res)
+{
+	int err;
+	struct mmc_command cmd;
+	struct mmc_data data;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	memset(res, 0, 64);
+	cmd.opcode = SD_SWITCH_FUNC;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.arg = mode << 31;
+	cmd.arg |= 0x00FFFFFF;
+	cmd.arg &= ~(0xF << grp);
+	cmd.arg |= value << grp;
+	cmd.data = &data;
+
+	data.data = res;
+	data.len = 64;
+	data.flags = MMC_DATA_READ;
+
+	err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+	return (err);
+}
+
+static int
+mmc_set_bus_width(struct mmc_softc *sc, uint16_t rca, int width)
+{
+	int err;
+
+	if (mmcbr_get_mode(sc->dev) == mode_sd) {
+		struct mmc_command cmd;
+
+		memset(&cmd, 0, sizeof(struct mmc_command));
+		cmd.opcode = ACMD_SET_BUS_WIDTH;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+		switch (width) {
+		case bus_width_1:
+			cmd.arg = SD_BUS_WIDTH_1;
+			break;
+		case bus_width_4:
+			cmd.arg = SD_BUS_WIDTH_4;
+			break;
+		default:
+			return (MMC_ERR_INVALID);
+		}
+		err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+	} else {
+		uint8_t	value;
+
+		switch (width) {
+		case bus_width_1:
+			value = EXT_CSD_BUS_WIDTH_1;
+			break;
+		case bus_width_4:
+			value = EXT_CSD_BUS_WIDTH_4;
+			break;
+		case bus_width_8:
+			value = EXT_CSD_BUS_WIDTH_8;
+			break;
+		default:
+			return (MMC_ERR_INVALID);
+		}
+		err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, value);
+	}
+	return (err);
+}
+
+static int
+mmc_set_timing(struct mmc_softc *sc, int timing)
+{
+	int err;
+	uint8_t	value;
+
+	switch (timing) {
+	case bus_timing_normal:
+		value = 0;
+		break;
+	case bus_timing_hs:
+		value = 1;
+		break;
+	default:
+		return (MMC_ERR_INVALID);
+	}
+	if (mmcbr_get_mode(sc->dev) == mode_sd) {
+		u_char switch_res[64];
+
+		err = mmc_sd_switch(sc, 1, 0, value, switch_res);
+	} else {
+		err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL,
+		    EXT_CSD_HS_TIMING, value);
+	}
+	return (err);
+}
+
+static int
+mmc_test_bus_width(struct mmc_softc *sc)
+{
+	struct mmc_command cmd;
+	struct mmc_data data;
+	int err;
+	uint8_t buf[8];
+	uint8_t	p8[8] =   { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	uint8_t	p8ok[8] = { 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	uint8_t	p4[4] =   { 0x5A, 0x00, 0x00, 0x00, };
+	uint8_t	p4ok[4] = { 0xA5, 0x00, 0x00, 0x00, };
+
+	if (mmcbr_get_caps(sc->dev) & MMC_CAP_8_BIT_DATA) {
+		mmcbr_set_bus_width(sc->dev, bus_width_8);
+		mmcbr_update_ios(sc->dev);
+
+		cmd.opcode = MMC_BUSTEST_W;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+		cmd.data = &data;
+
+		data.data = p8;
+		data.len = 8;
+		data.flags = MMC_DATA_WRITE;
+		mmc_wait_for_cmd(sc, &cmd, 0);
+		
+		cmd.opcode = MMC_BUSTEST_R;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+		cmd.data = &data;
+
+		data.data = buf;
+		data.len = 8;
+		data.flags = MMC_DATA_READ;
+		err = mmc_wait_for_cmd(sc, &cmd, 0);
+		
+		mmcbr_set_bus_width(sc->dev, bus_width_1);
+		mmcbr_update_ios(sc->dev);
+
+		if (err == MMC_ERR_NONE && memcmp(buf, p8ok, 8) == 0)
+			return (bus_width_8);
+	}
+
+	if (mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) {
+		mmcbr_set_bus_width(sc->dev, bus_width_4);
+		mmcbr_update_ios(sc->dev);
+
+		cmd.opcode = MMC_BUSTEST_W;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+		cmd.data = &data;
+
+		data.data = p4;
+		data.len = 4;
+		data.flags = MMC_DATA_WRITE;
+		mmc_wait_for_cmd(sc, &cmd, 0);
+		
+		cmd.opcode = MMC_BUSTEST_R;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+		cmd.data = &data;
+
+		data.data = buf;
+		data.len = 4;
+		data.flags = MMC_DATA_READ;
+		err = mmc_wait_for_cmd(sc, &cmd, 0);
+
+		mmcbr_set_bus_width(sc->dev, bus_width_1);
+		mmcbr_update_ios(sc->dev);
+
+		if (err == MMC_ERR_NONE && memcmp(buf, p4ok, 4) == 0)
+			return (bus_width_4);
+	}
+	return (bus_width_1);
+}
+
 static uint32_t
 mmc_get_bits(uint32_t *bits, int start, int size)
 {
@@ -506,8 +741,14 @@ mmc_decode_cid(int is_sd, uint32_t *raw_
 		cid->mdt_year = mmc_get_bits(raw_cid, 12, 8) + 2001;
 		cid->mdt_month = mmc_get_bits(raw_cid, 8, 4);
 	} else {
-		/* XXX write me */
-		panic("write mmc cid decoder");
+		cid->mid = mmc_get_bits(raw_cid, 120, 8);
+		cid->oid = mmc_get_bits(raw_cid, 104, 8);
+		for (i = 0; i < 6; i++)
+			cid->pnm[i] = mmc_get_bits(raw_cid, 96 - i * 8, 8);
+		cid->prv = mmc_get_bits(raw_cid, 48, 8);
+		cid->psn = mmc_get_bits(raw_cid, 16, 32);
+		cid->mdt_month = mmc_get_bits(raw_cid, 12, 4);
+		cid->mdt_year = mmc_get_bits(raw_cid, 8, 4) + 1997;
 	}
 }
 
@@ -563,12 +804,82 @@ mmc_decode_csd(int is_sd, uint32_t *raw_
 			csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 22, 4);
 			csd->write_bl_partial = mmc_get_bits(raw_csd, 21, 1);
 		} else if (v == 1) {
-			panic("Write SDHC CSD parser");
+			m = mmc_get_bits(raw_csd, 115, 4);
+			e = mmc_get_bits(raw_csd, 112, 3);
+			csd->tacc = exp[e] * mant[m] + 9 / 10;
+			csd->nsac = mmc_get_bits(raw_csd, 104, 8) * 100;
+			m = mmc_get_bits(raw_csd, 99, 4);
+			e = mmc_get_bits(raw_csd, 96, 3);
+			csd->tran_speed = exp[e] * 10000 * mant[m];
+			csd->ccc = mmc_get_bits(raw_csd, 84, 12);
+			csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 80, 4);
+			csd->read_bl_partial = mmc_get_bits(raw_csd, 79, 1);
+			csd->write_blk_misalign = mmc_get_bits(raw_csd, 78, 1);
+			csd->read_blk_misalign = mmc_get_bits(raw_csd, 77, 1);
+			csd->dsr_imp = mmc_get_bits(raw_csd, 76, 1);
+			csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 48, 22) + 1) *
+			    512 * 1024;
+			csd->erase_blk_en = mmc_get_bits(raw_csd, 46, 1);
+			csd->sector_size = mmc_get_bits(raw_csd, 39, 7);
+			csd->wp_grp_size = mmc_get_bits(raw_csd, 32, 7);
+			csd->wp_grp_enable = mmc_get_bits(raw_csd, 31, 1);
+			csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 26, 3);
+			csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 22, 4);
+			csd->write_bl_partial = mmc_get_bits(raw_csd, 21, 1);
 		} else 
 			panic("unknown SD CSD version");
 	} else {
-		panic("Write a MMC CSD parser");
+		csd->csd_structure = mmc_get_bits(raw_csd, 126, 2);
+		csd->spec_vers = mmc_get_bits(raw_csd, 122, 4);
+		m = mmc_get_bits(raw_csd, 115, 4);
+		e = mmc_get_bits(raw_csd, 112, 3);
+		csd->tacc = exp[e] * mant[m] + 9 / 10;
+		csd->nsac = mmc_get_bits(raw_csd, 104, 8) * 100;
+		m = mmc_get_bits(raw_csd, 99, 4);
+		e = mmc_get_bits(raw_csd, 96, 3);
+		csd->tran_speed = exp[e] * 10000 * mant[m];
+		csd->ccc = mmc_get_bits(raw_csd, 84, 12);
+		csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 80, 4);
+		csd->read_bl_partial = mmc_get_bits(raw_csd, 79, 1);
+		csd->write_blk_misalign = mmc_get_bits(raw_csd, 78, 1);
+		csd->read_blk_misalign = mmc_get_bits(raw_csd, 77, 1);
+		csd->dsr_imp = mmc_get_bits(raw_csd, 76, 1);
+		csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 59, 3)];
+		csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 56, 3)];
+		csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 53, 3)];
+		csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 50, 3)];
+		m = mmc_get_bits(raw_csd, 62, 12);
+		e = mmc_get_bits(raw_csd, 47, 3);
+		csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len;
+//		csd->erase_blk_en = mmc_get_bits(raw_csd, 46, 1);
+//		csd->sector_size = mmc_get_bits(raw_csd, 39, 7);
+		csd->wp_grp_size = mmc_get_bits(raw_csd, 32, 5);
+		csd->wp_grp_enable = mmc_get_bits(raw_csd, 31, 1);
+		csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 26, 3);
+		csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 22, 4);
+		csd->write_bl_partial = mmc_get_bits(raw_csd, 21, 1);
+	}
+}
+
+static void
+mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr)
+{
+	unsigned int scr_struct;
+	uint32_t tmp[4];
+
+	tmp[3] = raw_scr[1];
+	tmp[2] = raw_scr[0];
+
+	memset(scr, 0, sizeof(*scr));
+	
+	scr_struct = mmc_get_bits(tmp, 60, 4);
+	if (scr_struct != 0) {
+		printf("Unrecognised SCR structure version %d\n",
+		    scr_struct);
+		return;
 	}
+	scr->sda_vsn = mmc_get_bits(tmp, 56, 4);
+	scr->bus_widths = mmc_get_bits(tmp, 48, 4);
 }
 
 static int
@@ -602,6 +913,70 @@ mmc_send_csd(struct mmc_softc *sc, uint1
 }
 
 static int
+mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr)
+{
+	int err;
+	struct mmc_command cmd;
+	struct mmc_data data;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	memset(rawscr, 0, 8);
+	cmd.opcode = ACMD_SEND_SCR;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.arg = 0;
+	cmd.data = &data;
+
+	data.data = rawscr;
+	data.len = 8;
+	data.flags = MMC_DATA_READ;
+
+	err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+	rawscr[0] = be32toh(rawscr[0]);
+	rawscr[1] = be32toh(rawscr[1]);
+	return (err);
+}
+
+static int
+mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd)
+{
+	int err;
+	struct mmc_command cmd;
+	struct mmc_data data;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	memset(rawextcsd, 0, 512);
+	cmd.opcode = MMC_SEND_EXT_CSD;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.arg = 0;
+	cmd.data = &data;
+
+	data.data = rawextcsd;
+	data.len = 512;
+	data.flags = MMC_DATA_READ;
+
+	err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+	return (err);
+}
+
+static int
+mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp)
+{
+	struct mmc_command cmd;
+	int err;
+
+	cmd.opcode = MMC_SET_RELATIVE_ADDR;
+	cmd.arg = resp << 16;
+	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+	cmd.data = NULL;
+	err = mmc_wait_for_cmd(sc, &cmd, 0);
+	return (err);
+}
+
+static int
 mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp)
 {
 	struct mmc_command cmd;
@@ -623,6 +998,8 @@ mmc_discover_cards(struct mmc_softc *sc)
 	int err;
 	uint32_t resp;
 	device_t child;
+	uint16_t rca = 2;
+	u_char switch_res[64];
 
 	while (1) {
 		ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF,
@@ -636,22 +1013,75 @@ mmc_discover_cards(struct mmc_softc *sc)
 			device_printf(sc->dev, "Error reading CID %d\n", err);
 			break;
 		}
-		if (mmcbr_get_mode(sc->dev) == mode_sd) {
-			ivar->mode = mode_sd;
+		if (mmcbr_get_ro(sc->dev))
+			ivar->read_only = 1;
+		ivar->bus_width = bus_width_1;
+		ivar->mode = mmcbr_get_mode(sc->dev);
+		if (ivar->mode == mode_sd) {
 			mmc_decode_cid(1, ivar->raw_cid, &ivar->cid);
 			mmc_send_relative_addr(sc, &resp);
 			ivar->rca = resp >> 16;
-			if (mmcbr_get_ro(sc->dev))
-				ivar->read_only = 1;
+			/* Get card CSD. */
 			mmc_send_csd(sc, ivar->rca, ivar->raw_csd);
 			mmc_decode_csd(1, ivar->raw_csd, &ivar->csd);
-			printf("SD CARD: %lld bytes\n", (long long)
-			    ivar->csd.capacity);
+			if (ivar->csd.csd_structure > 0)
+				ivar->high_cap = 1;
+			ivar->tran_speed = ivar->csd.tran_speed;
+			/* Get card SCR. Card must be selected to fetch it. */
+			mmc_select_card(sc, ivar->rca);
+			mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr);
+			mmc_app_decode_scr(ivar->raw_scr, &ivar->scr);
+			/* Get card switch capabilities. */
+			if ((ivar->scr.sda_vsn >= 1) &&
+			    (ivar->csd.ccc & (1<<10))) {
+				mmc_sd_switch(sc, 0, 0, 0xF, switch_res);
+				if (switch_res[13] & 2) {
+					ivar->timing = bus_timing_hs;
+					ivar->hs_tran_speed = 50000000;
+				}
+			}
+			mmc_select_card(sc, 0);
+			/* Find max supported bus width. */
+			if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) &&
+			    (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4))
+				ivar->bus_width = bus_width_4;
+			/* Add device. */
 			child = device_add_child(sc->dev, NULL, -1);
 			device_set_ivars(child, ivar);
 			return;
 		}
-		panic("Write MMC card code here");
+		mmc_decode_cid(0, ivar->raw_cid, &ivar->cid);
+		ivar->rca = rca++;
+		mmc_set_relative_addr(sc, ivar->rca);
+		/* Get card CSD. */
+		mmc_send_csd(sc, ivar->rca, ivar->raw_csd);
+		mmc_decode_csd(0, ivar->raw_csd, &ivar->csd);
+		ivar->tran_speed = ivar->csd.tran_speed;
+		/* Only MMC >= 4.x cards support EXT_CSD. */
+		if (ivar->csd.spec_vers >= 4) {
+			/* Card must be selected to fetch EXT_CSD. */
+			mmc_select_card(sc, ivar->rca);
+			mmc_send_ext_csd(sc, ivar->raw_ext_csd);
+			/* Get card speed in high speed mode. */
+			ivar->timing = bus_timing_hs;
+			if (((uint8_t *)(ivar->raw_ext_csd))[EXT_CSD_CARD_TYPE]
+			    & EXT_CSD_CARD_TYPE_52)
+				ivar->hs_tran_speed = 52000000;
+			else if (((uint8_t *)(ivar->raw_ext_csd))[EXT_CSD_CARD_TYPE]
+			    & EXT_CSD_CARD_TYPE_26)
+				ivar->hs_tran_speed = 26000000;
+			else
+				ivar->hs_tran_speed = ivar->tran_speed;
+			/* Find max supported bus width. */
+			ivar->bus_width = mmc_test_bus_width(sc);
+			mmc_select_card(sc, 0);
+		} else {
+			ivar->bus_width = bus_width_1;
+			ivar->timing = bus_timing_normal;
+		}
+		/* Add device. */
+		child = device_add_child(sc->dev, NULL, -1);
+		device_set_ivars(child, ivar);
 	}
 	free(ivar, M_DEVBUF);
 }
@@ -661,6 +1091,7 @@ mmc_go_discovery(struct mmc_softc *sc)
 {
 	uint32_t ocr;
 	device_t dev;
+	int err;
 
 	dev = sc->dev;
 	if (mmcbr_get_power_mode(dev) != power_on) {
@@ -671,7 +1102,9 @@ mmc_go_discovery(struct mmc_softc *sc)
 		mmc_power_up(sc);
 		mmcbr_set_bus_mode(dev, pushpull);
 		mmc_idle_cards(sc);
-		if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
+		err = mmc_send_if_cond(sc, 1);
+		if (mmc_send_app_op_cond(sc, err?0:MMC_OCR_CCS, &ocr) !=
+		    MMC_ERR_NONE) {
 			/*
 			 * Failed, try MMC
 			 */
@@ -697,9 +1130,11 @@ mmc_go_discovery(struct mmc_softc *sc)
 	/*
 	 * Reselect the cards after we've idled them above.
 	 */
-	if (mmcbr_get_mode(dev) == mode_sd)
-		mmc_send_app_op_cond(sc, mmcbr_get_ocr(dev), NULL);
-	else
+	if (mmcbr_get_mode(dev) == mode_sd) {
+		err = mmc_send_if_cond(sc, 1);
+		mmc_send_app_op_cond(sc,
+		    (err?0:MMC_OCR_CCS)|mmcbr_get_ocr(dev), NULL);
+	} else
 		mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL);
 	mmc_discover_cards(sc);
 
@@ -712,22 +1147,47 @@ mmc_go_discovery(struct mmc_softc *sc)
 static int
 mmc_calculate_clock(struct mmc_softc *sc)
 {
-	int max_dtr = 0;
+	int max_dtr, max_hs_dtr, max_timing;
 	int nkid, i, f_min, f_max;
 	device_t *kids;
+	struct mmc_ivars *ivar;
 	
 	f_min = mmcbr_get_f_min(sc->dev);
 	f_max = mmcbr_get_f_max(sc->dev);
-	max_dtr = f_max;
+	max_dtr = max_hs_dtr = f_max;
+	if ((mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED))
+		max_timing = bus_timing_hs;
+	else
+		max_timing = bus_timing_normal;
 	if (device_get_children(sc->dev, &kids, &nkid) != 0)
 		panic("can't get children");
-	for (i = 0; i < nkid; i++)
-		if (mmc_get_tran_speed(kids[i]) < max_dtr)
-			max_dtr = mmc_get_tran_speed(kids[i]);
+	for (i = 0; i < nkid; i++) {
+		ivar = device_get_ivars(kids[i]);
+		if (ivar->timing < max_timing)
+			max_timing = ivar->timing;
+		if (ivar->tran_speed < max_dtr)
+			max_dtr = ivar->tran_speed;
+		if (ivar->hs_tran_speed < max_dtr)
+			max_hs_dtr = ivar->hs_tran_speed;
+	}
+	for (i = 0; i < nkid; i++) {
+		ivar = device_get_ivars(kids[i]);
+		if (ivar->timing == bus_timing_normal)
+			continue;
+		mmc_select_card(sc, ivar->rca);
+		mmc_set_timing(sc, max_timing);
+	}
+	mmc_select_card(sc, 0);
 	free(kids, M_TEMP);
-	device_printf(sc->dev, "setting transfer rate to %d.%03dMHz\n",
-	    max_dtr / 1000000, (max_dtr / 1000) % 1000);
-	return (max_dtr);
+	if (max_timing == bus_timing_hs)
+		max_dtr = max_hs_dtr;
+	device_printf(sc->dev, "setting transfer rate to %d.%03dMHz%s\n",
+	    max_dtr / 1000000, (max_dtr / 1000) % 1000,
+	    (max_timing == bus_timing_hs)?" with high speed timing":"");
+	mmcbr_set_timing(sc->dev, max_timing);
+	mmcbr_set_clock(sc->dev, max_dtr);
+	mmcbr_update_ios(sc->dev);
+	return max_dtr;
 }
 
 static void
@@ -741,8 +1201,7 @@ mmc_scan(struct mmc_softc *sc)
 	if (mmcbr_get_power_mode(dev) == power_on)
 		mmc_rescan_cards(sc);
 	mmc_go_discovery(sc);
-	mmcbr_set_clock(dev, mmc_calculate_clock(sc));
-	mmcbr_update_ios(dev);
+	mmc_calculate_clock(sc);
 
 	mmc_release_bus(dev, dev);
 	/* XXX probe/attach/detach children? */
@@ -774,6 +1233,9 @@ mmc_read_ivar(device_t bus, device_t chi
 	case MMC_IVAR_READ_ONLY:
 		*(int *)result = ivar->read_only;
 		break;
+	case MMC_IVAR_HIGH_CAP:
+		*(int *)result = ivar->high_cap;
+		break;
 	}
 	return (0);
 }

Modified: head/sys/dev/mmc/mmcbrvar.h
==============================================================================
--- head/sys/dev/mmc/mmcbrvar.h	Wed Oct  8 16:46:36 2008	(r183703)
+++ head/sys/dev/mmc/mmcbrvar.h	Wed Oct  8 17:35:41 2008	(r183704)
@@ -71,6 +71,7 @@ enum mmcbr_device_ivars {
     MMCBR_IVAR_POWER_MODE,
     MMCBR_IVAR_VDD,
     MMCBR_IVAR_CAPS,
+    MMCBR_IVAR_TIMING,
 //    MMCBR_IVAR_,
 };
 
@@ -92,6 +93,7 @@ MMCBR_ACCESSOR(ocr, OCR, int)
 MMCBR_ACCESSOR(power_mode, POWER_MODE, int)
 MMCBR_ACCESSOR(vdd, VDD, int)
 MMCBR_ACCESSOR(caps, CAPS, int)
+MMCBR_ACCESSOR(timing, TIMING, int)
 
 static int __inline
 mmcbr_update_ios(device_t dev)

Modified: head/sys/dev/mmc/mmcreg.h
==============================================================================
--- head/sys/dev/mmc/mmcreg.h	Wed Oct  8 16:46:36 2008	(r183703)
+++ head/sys/dev/mmc/mmcreg.h	Wed Oct  8 17:35:41 2008	(r183704)
@@ -86,7 +86,7 @@ struct mmc_command {
 #define	MMC_RSP_R2	(MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
 #define	MMC_RSP_R3	(MMC_RSP_PRESENT)
 #define	MMC_RSP_R6	(MMC_RSP_PRESENT | MMC_RSP_CRC)
-/* R7 -- new in sd 2.0 */
+#define	MMC_RSP_R7	(MMC_RSP_PRESENT | MMC_RSP_CRC)
 #define	MMC_RSP(x)	((x) & MMC_RSP_MASK)
 	uint32_t	retries;
 	uint32_t	error;
@@ -181,16 +181,23 @@ struct mmc_request {
 #define	SD_SEND_RELATIVE_ADDR	3
 #define	MMC_SET_DSR		4
 			/* reserved: 5 */
+#define	MMC_SWITCH_FUNC		6
+#define	 MMC_SWITCH_FUNC_CMDS	 0
+#define	 MMC_SWITCH_FUNC_SET	 1
+#define	 MMC_SWITCH_FUNC_CLR	 2
+#define	 MMC_SWITCH_FUNC_WR	 3
 #define	MMC_SELECT_CARD		7
 #define	MMC_DESELECT_CARD	7
-#define	MMC_SEND_IF_COND	8
+#define	MMC_SEND_EXT_CSD	8
+#define	SD_SEND_IF_COND		8
 #define	MMC_SEND_CSD		9
 #define	MMC_SEND_CID		10
 #define	MMC_READ_DAT_UNTIL_STOP	11
 #define	MMC_STOP_TRANSMISSION	12
 #define	MMC_SEND_STATUS		13
-			/* reserved: 14 */
+#define	MMC_BUSTEST_R		14
 #define	MMC_GO_INACTIVE_STATE	15
+#define	MMC_BUSTEST_W		19
 
 /* Class 2: Block oriented read commands */
 #define	MMC_SET_BLOCKLEN	16
@@ -277,6 +284,37 @@ struct mmc_request {
 #define	ACMD_SET_CLR_CARD_DETECT 42
 #define	ACMD_SEND_SCR		51
 
+/*
+ * EXT_CSD fields
+ */
+
+#define EXT_CSD_BUS_WIDTH	183	/* R/W */
+#define EXT_CSD_HS_TIMING	185	/* R/W */
+#define EXT_CSD_CARD_TYPE	196	/* RO */
+#define EXT_CSD_REV		192	/* RO */
+#define EXT_CSD_SEC_CNT		212	/* RO, 4 bytes */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+#define EXT_CSD_CMD_SET_NORMAL		1
+#define EXT_CSD_CMD_SET_SECURE		2
+#define EXT_CSD_CMD_SET_CPSECURE	4
+
+#define EXT_CSD_CARD_TYPE_26	1
+#define EXT_CSD_CARD_TYPE_52	2
+
+#define EXT_CSD_BUS_WIDTH_1	0
+#define EXT_CSD_BUS_WIDTH_4	1
+#define EXT_CSD_BUS_WIDTH_8	2
+
+/*
+ * SD bus widths
+ */
+#define SD_BUS_WIDTH_1		0
+#define SD_BUS_WIDTH_4		2
+
 /* OCR bits */
 
 /*
@@ -328,6 +366,7 @@ struct mmc_cid {
 struct mmc_csd 
 {
 	uint8_t csd_structure;
+	uint8_t spec_vers;
 	uint16_t ccc;
 	uint16_t tacc;
 	uint32_t nsac;
@@ -351,6 +390,14 @@ struct mmc_csd 
 	    wp_grp_enable:1;
 };
 
+struct mmc_scr
+{
+	unsigned char		sda_vsn;
+	unsigned char		bus_widths;
+#define SD_SCR_BUS_WIDTH_1	(1<<0)
+#define SD_SCR_BUS_WIDTH_4	(1<<2)
+};
+
 /*
  * Older versions of the MMC standard had a variable sector size.  However,
  * I've been able to find no old MMC or SD cards that have a non 512

Modified: head/sys/dev/mmc/mmcsd.c
==============================================================================
--- head/sys/dev/mmc/mmcsd.c	Wed Oct  8 16:46:36 2008	(r183703)
+++ head/sys/dev/mmc/mmcsd.c	Wed Oct  8 17:35:41 2008	(r183704)
@@ -106,6 +106,7 @@ static int
 mmcsd_probe(device_t dev)
 {
 
+	device_quiet(dev);
 	device_set_desc(dev, "MMC/SD Memory Card");
 	return (0);
 }
@@ -256,7 +257,9 @@ mmcsd_task(void *arg)
 				else
 					cmd.opcode = MMC_WRITE_BLOCK;
 			}
-			cmd.arg = block << 9;
+			cmd.arg = block;
+			if (!mmc_get_high_cap(dev))
+				cmd.arg <<= 9;
 			cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 			data.data = vaddr;
 			data.mrq = &req;

Modified: head/sys/dev/mmc/mmcvar.h
==============================================================================
--- head/sys/dev/mmc/mmcvar.h	Wed Oct  8 16:46:36 2008	(r183703)
+++ head/sys/dev/mmc/mmcvar.h	Wed Oct  8 17:35:41 2008	(r183704)
@@ -62,6 +62,7 @@ enum mmc_device_ivars {
     MMC_IVAR_SECTOR_SIZE,
     MMC_IVAR_TRAN_SPEED,
     MMC_IVAR_READ_ONLY,
+    MMC_IVAR_HIGH_CAP,
 //    MMC_IVAR_,
 };
 
@@ -77,5 +78,6 @@ MMC_ACCESSOR(rca, RCA, int)
 MMC_ACCESSOR(sector_size, SECTOR_SIZE, int)
 MMC_ACCESSOR(tran_speed, TRAN_SPEED, int)
 MMC_ACCESSOR(read_only, READ_ONLY, int)
+MMC_ACCESSOR(high_cap, HIGH_CAP, int)
 
 #endif /* DEV_MMC_MMCVAR_H */


More information about the svn-src-all mailing list