svn commit: r186786 - stable/7/sys/dev/mmc

Alexander Motin mav at FreeBSD.org
Mon Jan 5 19:40:11 UTC 2009


Author: mav
Date: Mon Jan  5 19:40:09 2009
New Revision: 186786
URL: http://svn.freebsd.org/changeset/base/186786

Log:
  Sync MMC/SD subsystem with HEAD.
  Add support for MMC and SDHC cards, high speed timing, wide bus, multiblock
  transfers and many other features.

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

Modified: stable/7/sys/dev/mmc/bridge.h
==============================================================================
--- stable/7/sys/dev/mmc/bridge.h	Mon Jan  5 17:38:03 2009	(r186785)
+++ stable/7/sys/dev/mmc/bridge.h	Mon Jan  5 19:40:09 2009	(r186786)
@@ -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: stable/7/sys/dev/mmc/mmc.c
==============================================================================
--- stable/7/sys/dev/mmc/mmc.c	Mon Jan  5 17:38:03 2009	(r186785)
+++ stable/7/sys/dev/mmc/mmc.c	Mon Jan  5 19:40:09 2009	(r186786)
@@ -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,10 +83,23 @@ 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 */
+	uint32_t raw_sd_status[16];	/* Raw bits of the SD_STATUS */
 	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 */
+	struct mmc_sd_status sd_status;	/* SD_STATUS 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 (block addressed) */
+	uint32_t sec_count;	/* Card capacity in 512byte blocks */
+	uint32_t tran_speed;	/* Max speed in normal mode */
+	uint32_t hs_tran_speed;	/* Max speed in high speed mode */
+	uint32_t erase_sector;	/* Card native erase sector size */
 };
 
 #define CMD_RETRIES	3
@@ -94,21 +108,32 @@ struct mmc_ivars {
 static int mmc_probe(device_t dev);
 static int mmc_attach(device_t dev);
 static int mmc_detach(device_t dev);
+static int mmc_suspend(device_t dev);
+static int mmc_resume(device_t dev);
 
 #define MMC_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
 #define	MMC_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
-#define MMC_LOCK_INIT(_sc) \
-	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
+#define MMC_LOCK_INIT(_sc)					\
+	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev),	\
 	    "mmc", MTX_DEF)
 #define MMC_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
 #define MMC_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
 #define MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
 
+static int mmc_calculate_clock(struct mmc_softc *sc);
 static void mmc_delayed_attach(void *);
+static void mmc_power_down(struct mmc_softc *sc);
 static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd,
     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_card_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_scan(struct mmc_softc *sc);
+static int mmc_delete_cards(struct mmc_softc *sc);
 
 static void
 mmc_ms_delay(int ms)
@@ -120,7 +145,7 @@ static int
 mmc_probe(device_t dev)
 {
 
-	device_set_desc(dev, "mmc/sd bus");
+	device_set_desc(dev, "MMC/SD bus");
 	return (0);
 }
 
@@ -145,35 +170,47 @@ static int
 mmc_detach(device_t dev)
 {
 	struct mmc_softc *sc = device_get_softc(dev);
-	device_t *kids;
-	int i, nkid;
-
-	/* kill children [ph33r].  -sorbo */
-	if (device_get_children(sc->dev, &kids, &nkid) != 0)
-		return 0;
-	for (i = 0; i < nkid; i++) {
-		device_t kid = kids[i];
-		void *ivar = device_get_ivars(kid);
-		
-		device_detach(kid);
-		device_delete_child(sc->dev, kid);
-		free(ivar, M_DEVBUF);
-	}
-	free(kids, M_TEMP);
+	int err;
 
+	if ((err = mmc_delete_cards(sc)) != 0)
+		return (err);
+	mmc_power_down(sc);
 	MMC_LOCK_DESTROY(sc);
 
-	return 0;
+	return (0);
+}
+
+static int
+mmc_suspend(device_t dev)
+{
+	struct mmc_softc *sc = device_get_softc(dev);
+	int err;
+
+	err = bus_generic_suspend(dev);
+	if (err)
+	        return (err);
+	mmc_power_down(sc);
+	return (0);
+}
+
+static int
+mmc_resume(device_t dev)
+{
+	struct mmc_softc *sc = device_get_softc(dev);
+
+	mmc_scan(sc);
+	return (bus_generic_resume(dev));
 }
 
 static int
 mmc_acquire_bus(device_t busdev, device_t dev)
 {
 	struct mmc_softc *sc;
+	struct mmc_ivars *ivar;
 	int err;
 	int rca;
 
-	err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), dev);
+	err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev);
 	if (err)
 		return (err);
 	sc = device_get_softc(busdev);
@@ -184,24 +221,36 @@ mmc_acquire_bus(device_t busdev, device_
 	MMC_UNLOCK(sc);
 
 	if (busdev != dev) {
-		// Keep track of the last rca that we've selected.  If
-		// we're asked to do it again, don't.  We never unselect
-		// unless the bus code itself wants the mmc bus.
+		/*
+		 * Keep track of the last rca that we've selected.  If
+		 * we're asked to do it again, don't.  We never
+		 * unselect unless the bus code itself wants the mmc
+		 * bus, and constantly reselecting causes problems.
+		 */
 		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);
+			if (bootverbose) {
+				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_card_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 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);
@@ -221,7 +270,7 @@ mmc_release_bus(device_t busdev, device_
 	if (sc->owner != dev)
 		panic("mmc: you don't own the bus.  game over.");
 	MMC_UNLOCK(sc);
-	err = MMCBR_RELEASE_HOST(device_get_parent(busdev), dev);
+	err = MMCBR_RELEASE_HOST(device_get_parent(busdev), busdev);
 	if (err)
 		return (err);
 	MMC_LOCK(sc);
@@ -230,17 +279,11 @@ mmc_release_bus(device_t busdev, device_
 	return (0);
 }
 
-static void
-mmc_rescan_cards(struct mmc_softc *sc)
-{
-	/* XXX: Look at the children and see if they respond to status */
-}
-
 static uint32_t
 mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr)
 {
-    // XXX
-	return ocr;
+
+	return (ocr & MMC_OCR_VOLTAGE);
 }
 
 static int
@@ -250,7 +293,7 @@ mmc_highest_voltage(uint32_t ocr)
 
 	for (i = 30; i >= 0; i--)
 		if (ocr & (1 << i))
-			return i;
+			return (i);
 	return (-1);
 }
 
@@ -259,31 +302,25 @@ mmc_wakeup(struct mmc_request *req)
 {
 	struct mmc_softc *sc;
 
-//	printf("Wakeup for req %p done_data %p\n", req, req->done_data);
 	sc = (struct mmc_softc *)req->done_data;
 	MMC_LOCK(sc);
 	req->flags |= MMC_REQ_DONE;
-	wakeup(req);
 	MMC_UNLOCK(sc);
+	wakeup(req);
 }
 
 static int
 mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req)
 {
-	int err;
 
 	req->done = mmc_wakeup;
 	req->done_data = sc;
-//	printf("Submitting request %p sc %p\n", req, sc);
 	MMCBR_REQUEST(device_get_parent(sc->dev), sc->dev, req);
 	MMC_LOCK(sc);
-	do {
-		err = msleep(req, &sc->sc_mtx, PZERO | PCATCH, "mmcreq",
-		    hz / 10);
-	} while (!(req->flags & MMC_REQ_DONE) && err == EAGAIN);
-//	printf("Request %p done with error %d\n", req, err);
+	while ((req->flags & MMC_REQ_DONE) == 0)
+		msleep(req, &sc->sc_mtx, 0, "mmcreq", 0);
 	MMC_UNLOCK(sc);
-	return (err);
+	return (0);
 }
 
 static int
@@ -291,7 +328,7 @@ mmc_wait_for_request(device_t brdev, dev
 {
 	struct mmc_softc *sc = device_get_softc(brdev);
 
-	return mmc_wait_for_req(sc, req);
+	return (mmc_wait_for_req(sc, req));
 }
 
 static int
@@ -302,9 +339,8 @@ mmc_wait_for_cmd(struct mmc_softc *sc, s
 	memset(&mreq, 0, sizeof(mreq));
 	memset(cmd->resp, 0, sizeof(cmd->resp));
 	cmd->retries = retries;
-	cmd->data = NULL;
 	mreq.cmd = cmd;
-//	printf("CMD: %x ARG %x\n", cmd->opcode, cmd->arg);
+/*	printf("CMD: %x ARG %x\n", cmd->opcode, cmd->arg); */
 	mmc_wait_for_req(sc, &mreq);
 	return (cmd->error);
 }
@@ -320,6 +356,7 @@ mmc_wait_for_app_cmd(struct mmc_softc *s
 		appcmd.opcode = MMC_APP_CMD;
 		appcmd.arg = rca << 16;
 		appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+		appcmd.data = NULL;
 		mmc_wait_for_cmd(sc, &appcmd, 0);
 		err = appcmd.error;
 		if (err != MMC_ERR_NONE)
@@ -345,6 +382,7 @@ mmc_wait_for_command(struct mmc_softc *s
 	cmd.opcode = opcode;
 	cmd.arg = arg;
 	cmd.flags = flags;
+	cmd.data = NULL;
 	err = mmc_wait_for_cmd(sc, &cmd, retries);
 	if (err)
 		return (err);
@@ -374,6 +412,7 @@ mmc_idle_cards(struct mmc_softc *sc)
 	cmd.opcode = MMC_GO_IDLE_STATE;
 	cmd.arg = 0;
 	cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+	cmd.data = NULL;
 	mmc_wait_for_cmd(sc, &cmd, 0);
 	mmc_ms_delay(1);
 
@@ -392,19 +431,21 @@ mmc_send_app_op_cond(struct mmc_softc *s
 	cmd.opcode = ACMD_SD_SEND_OP_COND;
 	cmd.arg = ocr;
 	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.data = NULL;
 
 	for (i = 0; i < 100; i++) {
 		err = mmc_wait_for_app_cmd(sc, 0, &cmd, CMD_RETRIES);
 		if (err != MMC_ERR_NONE)
 			break;
-		if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || ocr == 0)
+		if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
+		    (ocr & MMC_OCR_VOLTAGE) == 0)
 			break;
 		err = MMC_ERR_TIMEOUT;
 		mmc_ms_delay(10);
 	}
 	if (rocr && err == MMC_ERR_NONE)
 		*rocr = cmd.resp[0];
-	return err;
+	return (err);
 }
 
 static int
@@ -417,19 +458,37 @@ mmc_send_op_cond(struct mmc_softc *sc, u
 	cmd.opcode = MMC_SEND_OP_COND;
 	cmd.arg = ocr;
 	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.data = NULL;
 
 	for (i = 0; i < 100; i++) {
 		err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
 		if (err != MMC_ERR_NONE)
 			break;
-		if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || ocr == 0)
+		if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
+		    (ocr & MMC_OCR_VOLTAGE) == 0)
 			break;
 		err = MMC_ERR_TIMEOUT;
 		mmc_ms_delay(10);
 	}
 	if (rocr && err == MMC_ERR_NONE)
 		*rocr = cmd.resp[0];
-	return err;
+	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
@@ -448,43 +507,269 @@ 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);
 }
 
-// I wonder if the following is endian safe.
+static void
+mmc_power_down(struct mmc_softc *sc)
+{
+	device_t dev = sc->dev;
+
+	mmcbr_set_bus_mode(dev, opendrain);
+	mmcbr_set_chip_select(dev, cs_dontcare);
+	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)
+{
+	int flags;
+
+	flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC;
+	return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16,
+	    flags, 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 * 4));
+	cmd.arg |= value << (grp * 4);
+	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_card_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)
+mmc_get_bits(uint32_t *bits, int bit_len, int start, int size)
 {
-	const int i = 3 - (start / 32);
+	const int i = (bit_len / 32) - (start / 32) - 1;
 	const int shift = start & 31;
 	uint32_t retval = bits[i] >> shift;
 	if (size + shift > 32)
 		retval |= bits[i - 1] << (32 - shift);
-	return retval & ((1 << size) - 1);
+	return (retval & ((1 << size) - 1));
 }
 
 static void
-mmc_decode_cid(int is_sd, uint32_t *raw_cid, struct mmc_cid *cid)
+mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid)
 {
 	int i;
 
+	/* There's no version info, so we take it on faith */
 	memset(cid, 0, sizeof(*cid));
-	if (is_sd) {
-		/* There's no version info, so we take it on faith */
-		cid->mid = mmc_get_bits(raw_cid, 120, 8);
-		cid->oid = mmc_get_bits(raw_cid, 104, 16);
-		for (i = 0; i < 5; i++)
-			cid->pnm[i] = mmc_get_bits(raw_cid, 96 - i * 8, 8);
-		cid->prv = mmc_get_bits(raw_cid, 56, 8);
-		cid->psn = mmc_get_bits(raw_cid, 24, 32);
-		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, 128, 120, 8);
+	cid->oid = mmc_get_bits(raw_cid, 128, 104, 16);
+	for (i = 0; i < 5; i++)
+		cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8);
+	cid->prv = mmc_get_bits(raw_cid, 128, 56, 8);
+	cid->psn = mmc_get_bits(raw_cid, 128, 24, 32);
+	cid->mdt_year = mmc_get_bits(raw_cid, 128, 12, 8) + 2001;
+	cid->mdt_month = mmc_get_bits(raw_cid, 128, 8, 4);
+}
+
+static void
+mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid)
+{
+	int i;
+
+	/* There's no version info, so we take it on faith */
+	memset(cid, 0, sizeof(*cid));
+	cid->mid = mmc_get_bits(raw_cid, 128, 120, 8);
+	cid->oid = mmc_get_bits(raw_cid, 128, 104, 8);
+	for (i = 0; i < 6; i++)
+		cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8);
+	cid->prv = mmc_get_bits(raw_cid, 128, 48, 8);
+	cid->psn = mmc_get_bits(raw_cid, 128, 16, 32);
+	cid->mdt_month = mmc_get_bits(raw_cid, 128, 12, 4);
+	cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4) + 1997;
 }
 
 static const int exp[8] = {
@@ -501,50 +786,142 @@ static const int cur_max[8] = {
 };
 
 static void
-mmc_decode_csd(int is_sd, uint32_t *raw_csd, struct mmc_csd *csd)
+mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd)
 {
 	int v;
 	int m;
 	int e;
 
 	memset(csd, 0, sizeof(*csd));
-	if (is_sd) {
-		csd->csd_structure = v = mmc_get_bits(raw_csd, 126, 2);
-		if (v == 0) {
-			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, 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 if (v == 1) {
-			panic("Write SDHC CSD parser");
-		} else 
-			panic("unknown SD CSD version");
-	} else {
-		panic("Write a MMC CSD parser");
+	csd->csd_structure = v = mmc_get_bits(raw_csd, 128, 126, 2);
+	if (v == 0) {
+		m = mmc_get_bits(raw_csd, 128, 115, 4);
+		e = mmc_get_bits(raw_csd, 128, 112, 3);
+		csd->tacc = exp[e] * mant[m] + 9 / 10;
+		csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100;
+		m = mmc_get_bits(raw_csd, 128, 99, 4);
+		e = mmc_get_bits(raw_csd, 128, 96, 3);
+		csd->tran_speed = exp[e] * 10000 * mant[m];
+		csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12);
+		csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4);
+		csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1);
+		csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
+		csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
+		csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
+		csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)];
+		csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)];
+		csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)];
+		csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)];
+		m = mmc_get_bits(raw_csd, 128, 62, 12);
+		e = mmc_get_bits(raw_csd, 128, 47, 3);
+		csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len;
+		csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1);
+		csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1;
+		csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7);
+		csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1);
+		csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
+		csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
+		csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+	} else if (v == 1) {
+		m = mmc_get_bits(raw_csd, 128, 115, 4);
+		e = mmc_get_bits(raw_csd, 128, 112, 3);
+		csd->tacc = exp[e] * mant[m] + 9 / 10;
+		csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100;
+		m = mmc_get_bits(raw_csd, 128, 99, 4);
+		e = mmc_get_bits(raw_csd, 128, 96, 3);
+		csd->tran_speed = exp[e] * 10000 * mant[m];
+		csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12);
+		csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4);
+		csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1);
+		csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
+		csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
+		csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
+		csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + 1) *
+		    512 * 1024;
+		csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1);
+		csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1;
+		csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7);
+		csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1);
+		csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
+		csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
+		csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+	} else 
+		panic("unknown SD CSD version");
+}
+
+static void
+mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd)
+{
+	int m;
+	int e;
+
+	memset(csd, 0, sizeof(*csd));
+	csd->csd_structure = mmc_get_bits(raw_csd, 128, 126, 2);
+	csd->spec_vers = mmc_get_bits(raw_csd, 128, 122, 4);
+	m = mmc_get_bits(raw_csd, 128, 115, 4);
+	e = mmc_get_bits(raw_csd, 128, 112, 3);
+	csd->tacc = exp[e] * mant[m] + 9 / 10;
+	csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100;
+	m = mmc_get_bits(raw_csd, 128, 99, 4);
+	e = mmc_get_bits(raw_csd, 128, 96, 3);
+	csd->tran_speed = exp[e] * 10000 * mant[m];
+	csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12);
+	csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4);
+	csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1);
+	csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
+	csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
+	csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
+	csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)];
+	csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)];
+	csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)];
+	csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)];
+	m = mmc_get_bits(raw_csd, 128, 62, 12);
+	e = mmc_get_bits(raw_csd, 128, 47, 3);
+	csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len;
+	csd->erase_blk_en = 0;
+	csd->erase_sector = (mmc_get_bits(raw_csd, 128, 42, 5) + 1) *
+	    (mmc_get_bits(raw_csd, 128, 37, 5) + 1);
+	csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 5);
+	csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1);
+	csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
+	csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
+	csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+}
+
+static void
+mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr)
+{
+	unsigned int scr_struct;
+
+	memset(scr, 0, sizeof(*scr));
+
+	scr_struct = mmc_get_bits(raw_scr, 64, 60, 4);
+	if (scr_struct != 0) {
+		printf("Unrecognised SCR structure version %d\n",
+		    scr_struct);
+		return;
 	}
+	scr->sda_vsn = mmc_get_bits(raw_scr, 64, 56, 4);
+	scr->bus_widths = mmc_get_bits(raw_scr, 64, 48, 4);
+}
+
+static void
+mmc_app_decode_sd_status(uint32_t *raw_sd_status,
+    struct mmc_sd_status *sd_status)
+{
+
+	memset(sd_status, 0, sizeof(*sd_status));
+
+	sd_status->bus_width = mmc_get_bits(raw_sd_status, 512, 510, 2);
+	sd_status->secured_mode = mmc_get_bits(raw_sd_status, 512, 509, 1);
+	sd_status->card_type = mmc_get_bits(raw_sd_status, 512, 480, 16);
+	sd_status->prot_area = mmc_get_bits(raw_sd_status, 512, 448, 12);
+	sd_status->speed_class = mmc_get_bits(raw_sd_status, 512, 440, 8);
+	sd_status->perf_move = mmc_get_bits(raw_sd_status, 512, 432, 8);
+	sd_status->au_size = mmc_get_bits(raw_sd_status, 512, 428, 4);
+	sd_status->erase_size = mmc_get_bits(raw_sd_status, 512, 408, 16);
+	sd_status->erase_timeout = mmc_get_bits(raw_sd_status, 512, 402, 6);
+	sd_status->erase_offset = mmc_get_bits(raw_sd_status, 512, 400, 2);
 }
 
 static int
@@ -556,6 +933,7 @@ mmc_all_send_cid(struct mmc_softc *sc, u
 	cmd.opcode = MMC_ALL_SEND_CID;
 	cmd.arg = 0;
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+	cmd.data = NULL;
 	err = mmc_wait_for_cmd(sc, &cmd, 0);
 	memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t));
 	return (err);
@@ -570,12 +948,103 @@ mmc_send_csd(struct mmc_softc *sc, uint1
 	cmd.opcode = MMC_SEND_CSD;
 	cmd.arg = rca << 16;
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+	cmd.data = NULL;
 	err = mmc_wait_for_cmd(sc, &cmd, 0);
 	memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t));
 	return (err);
 }
 
 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_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus)
+{
+	int err, i;
+	struct mmc_command cmd;
+	struct mmc_data data;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	memset(rawsdstatus, 0, 64);
+	cmd.opcode = ACMD_SD_STATUS;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.arg = 0;
+	cmd.data = &data;
+
+	data.data = rawsdstatus;
+	data.len = 64;
+	data.flags = MMC_DATA_READ;
+
+	err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+	for (i = 0; i < 16; i++)
+	    rawsdstatus[i] = be32toh(rawsdstatus[i]);
+	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;
@@ -584,6 +1053,7 @@ mmc_send_relative_addr(struct mmc_softc 
 	cmd.opcode = SD_SEND_RELATIVE_ADDR;
 	cmd.arg = 0;
 	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+	cmd.data = NULL;
 	err = mmc_wait_for_cmd(sc, &cmd, 0);
 	*resp = cmd.resp[0];
 	return (err);
@@ -592,39 +1062,182 @@ mmc_send_relative_addr(struct mmc_softc 
 static void
 mmc_discover_cards(struct mmc_softc *sc)
 {
-	struct mmc_ivars *ivar;
-	int err;
-	uint32_t resp;
+	struct mmc_ivars *ivar = NULL;
+	device_t *devlist;
+	int err, i, devcount, newcard;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-stable mailing list