Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177549573
D60884.1794688836.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
11 KB
Referenced Files
None
Subscribers
None
D60884.1794688836.diff
View Options
diff --git a/sys/cam/mmc/mmc_da.c b/sys/cam/mmc/mmc_da.c
--- a/sys/cam/mmc/mmc_da.c
+++ b/sys/cam/mmc/mmc_da.c
@@ -1031,19 +1031,24 @@
}
/*
- * Send the host settings of the ccb (ios, with ios_valid telling which ones
- * to change) to the controller.
+ * Change the host settings. The request is queued to the SIM, which may do
+ * slow work that can sleep (changing clocks, tuning) outside of the CAM
+ * locks, so wait for it to complete. Return an MMC error code.
*/
-static void
+#define SDDA_HOST_IOS_TIMEOUT 10000 /* ms; tuning can take a while */
+
+static int
sdda_set_host_ios(union ccb *ccb)
{
- ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ ccb->ccb_h.func_code = XPT_MMC_SET_TRAN_SETTINGS;
ccb->ccb_h.flags = CAM_DIR_NONE;
ccb->ccb_h.retry_count = 0;
- ccb->ccb_h.timeout = 100;
- ccb->ccb_h.cbfcnp = NULL;
- xpt_action(ccb);
+ ccb->ccb_h.timeout = SDDA_HOST_IOS_TIMEOUT;
+ cam_periph_runccb(ccb, NULL, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ return (MMC_ERR_FAILED);
+ return (MMC_ERR_NONE);
}
static int
@@ -1052,27 +1057,40 @@
enum mmc_bus_timing timing)
{
u_char switch_res[64];
- int err;
+ int err, host_err;
uint8_t value;
struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
("mmc_set_timing(timing=%d)", timing));
- switch (timing) {
- case bus_timing_normal:
- value = 0;
- break;
- case bus_timing_hs:
- value = 1;
- break;
- default:
- return (MMC_ERR_INVALID);
- }
if (mmcp->card_features & CARD_FEATURE_MMC) {
+ switch (timing) {
+ case bus_timing_normal:
+ value = EXT_CSD_HS_TIMING_BC;
+ break;
+ case bus_timing_hs:
+ value = EXT_CSD_HS_TIMING_HS;
+ break;
+ case bus_timing_mmc_hs200:
+ value = EXT_CSD_HS_TIMING_HS200;
+ break;
+ default:
+ return (MMC_ERR_INVALID);
+ }
err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, value, softc->cmd6_time);
} else {
+ switch (timing) {
+ case bus_timing_normal:
+ value = 0;
+ break;
+ case bus_timing_hs:
+ value = 1;
+ break;
+ default:
+ return (MMC_ERR_INVALID);
+ }
err = mmc_sd_switch(periph, ccb, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, value, switch_res);
}
@@ -1081,9 +1099,9 @@
cts = &ccb->cts.proto_specific.mmc;
cts->ios.timing = timing;
cts->ios_valid = MMC_BT;
- sdda_set_host_ios(ccb);
+ host_err = sdda_set_host_ios(ccb);
- return (err);
+ return (err != MMC_ERR_NONE ? err : host_err);
}
static void
@@ -1106,7 +1124,7 @@
xpt_free_ccb(new_ccb);
}
-static void
+static int
sdda_set_bus_width(struct cam_periph *periph, union ccb *ccb, int width)
{
struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
@@ -1145,14 +1163,51 @@
if (err != MMC_ERR_NONE) {
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Error %d when setting bus width on the card\n", err));
- return;
+ return (err);
}
/* Now card is done, set the host to the same width */
struct ccb_trans_settings_mmc *cts;
cts = &ccb->cts.proto_specific.mmc;
cts->ios.bus_width = width;
cts->ios_valid = MMC_BW;
- sdda_set_host_ios(ccb);
+ return (sdda_set_host_ios(ccb));
+}
+
+static int
+sdda_set_host_clock(union ccb *ccb, int clock)
+{
+ struct ccb_trans_settings_mmc *cts;
+
+ cts = &ccb->cts.proto_specific.mmc;
+ cts->ios.clock = clock;
+ cts->ios_valid = MMC_CLK;
+ return (sdda_set_host_ios(ccb));
+}
+
+/*
+ * Run HS200 at the maximum clock advertised by the host, unless
+ * hw.mmc.hs200_clock asks for a different one (for testing, or to work
+ * around a host that is unreliable at its advertised maximum).
+ */
+#define SDDA_HS200_CLOCK_DEFAULT 0
+static u_long sdda_hs200_clock = SDDA_HS200_CLOCK_DEFAULT;
+TUNABLE_ULONG("hw.mmc.hs200_clock", &sdda_hs200_clock);
+
+/*
+ * Switch an eMMC card to HS200 (following "6.6.2.2 HS200 timing mode
+ * selection" of the eMMC specification v5.1). The card must already use
+ * an 8-bit bus; the host performs the tuning when it is asked to switch to
+ * the 200MHz clock.
+ */
+static int
+sdda_set_hs200(struct cam_periph *periph, union ccb *ccb, int clock)
+{
+ int err;
+
+ err = mmc_set_timing(periph, ccb, bus_timing_mmc_hs200);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ return (sdda_set_host_clock(ccb, clock));
}
static inline const char
@@ -1223,6 +1278,7 @@
int err;
int host_f_max;
uint8_t card_type;
+ bool use_hs200 = false;
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdda_start_init\n"));
/* periph was held for us when this task was enqueued */
@@ -1378,9 +1434,16 @@
setbit(&softc->vccq_180, bus_timing_mmc_hs200);
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Card supports HS200 at 1.8V\n"));
}
+ /* Check whether both ends support HS200. */
+ if (isset(&softc->timings, bus_timing_mmc_hs200) &&
+ (host_caps & MMC_CAP_MMC_HS200_180) != 0)
+ use_hs200 = true;
}
}
+ /* A host whose maximum is exactly 100MHz can still run HS200 there. */
+ use_hs200 = use_hs200 && host_f_max >= SD_SDR50_MAX;
int f_max;
+ int hs200_clock;
finish_hs_tests:
f_max = min(host_f_max, softc->card_f_max);
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Set SD freq to %d MHz (min out of host f=%d MHz and card f=%d MHz)\n", f_max / 1000000, host_f_max / 1000000, softc->card_f_max / 1000000));
@@ -1395,8 +1458,19 @@
}
/* If possible, set lower-level signaling */
enum mmc_bus_timing timing;
- /* FIXME: MMCCAM supports max. bus_timing_mmc_ddr52 at the moment. */
- for (timing = bus_timing_mmc_ddr52; timing > bus_timing_normal; timing--) {
+ if (use_hs200) {
+ /* HS200/HS400 only exist at 1.8V (or 1.2V) signaling. */
+ cts->ios.vccq = vccq_180;
+ cts->ios_valid = MMC_VCCQ;
+ if (sdda_set_host_ios(start_ccb) != MMC_ERR_NONE) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Cannot set VCCQ to 1.8V, not using HS200\n"));
+ use_hs200 = false;
+ }
+ }
+ for (timing = bus_timing_mmc_ddr52;
+ timing > bus_timing_normal && !use_hs200;
+ timing--) {
if (isset(&softc->vccq_120, timing)) {
/* Set VCCQ = 1.2V */
cts->ios.vccq = vccq_120;
@@ -1446,7 +1520,32 @@
bus_width_str(desired_bus_width),
bus_width_str(max_host_bus_width),
bus_width_str(max_card_bus_width)));
- sdda_set_bus_width(periph, start_ccb, desired_bus_width);
+ err = sdda_set_bus_width(periph, start_ccb, desired_bus_width);
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Cannot set bus width to %s, err %d\n",
+ bus_width_str(desired_bus_width), err));
+ }
+
+ hs200_clock = (int)sdda_hs200_clock;
+ if (hs200_clock == 0)
+ hs200_clock = host_f_max;
+ if (use_hs200) {
+ err = sdda_set_hs200(periph, start_ccb, hs200_clock);
+ } else
+ err = MMC_ERR_NONE;
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Cannot switch to HS200, err %d, falling back to "
+ "high speed\n", err));
+ sdda_set_host_clock(start_ccb, MMC_TYPE_HS_52_MAX);
+ mmc_set_timing(periph, start_ccb, bus_timing_hs);
+ sdda_set_bus_width(periph, start_ccb, desired_bus_width);
+ } else if (use_hs200) {
+ softc->card_f_max = hs200_clock;
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Card switched to HS200\n"));
+ }
softc->state = SDDA_STATE_NORMAL;
@@ -1930,6 +2029,15 @@
/* Complete partition switch */
softc->state = SDDA_STATE_NORMAL;
if (error != 0) {
+ /*
+ * The switch failed. Fail the I/O queued for the
+ * requested partition, otherwise it would be retried
+ * forever.
+ */
+ if (softc->part[softc->part_requested] != NULL)
+ bioq_flush(&softc->part[
+ softc->part_requested]->bio_queue,
+ NULL, EIO);
/* TODO: Unpause retune if accessing RPMB */
xpt_release_ccb(done_ccb);
xpt_schedule(periph, CAM_PRIORITY_NORMAL);
diff --git a/sys/cam/mmc/mmc_sim.h b/sys/cam/mmc/mmc_sim.h
--- a/sys/cam/mmc/mmc_sim.h
+++ b/sys/cam/mmc/mmc_sim.h
@@ -36,6 +36,7 @@
struct cam_sim *sim;
device_t dev;
struct task sim_task;
+ struct taskqueue *tq;
union ccb *ccb;
};
diff --git a/sys/cam/mmc/mmc_sim.c b/sys/cam/mmc/mmc_sim.c
--- a/sys/cam/mmc/mmc_sim.c
+++ b/sys/cam/mmc/mmc_sim.c
@@ -52,36 +52,42 @@
{
struct mmc_sim *mmc_sim;
struct ccb_trans_settings *cts;
+ union ccb *ccb;
int rv;
mmc_sim = arg;
- if (mmc_sim->ccb == NULL)
+ ccb = mmc_sim->ccb;
+ if (ccb == NULL)
return;
- cts = &mmc_sim->ccb->cts;
- switch (mmc_sim->ccb->ccb_h.func_code) {
+ cts = &ccb->cts;
+ switch (ccb->ccb_h.func_code) {
case XPT_MMC_GET_TRAN_SETTINGS:
rv = MMC_SIM_GET_TRAN_SETTINGS(mmc_sim->dev, &cts->proto_specific.mmc);
if (rv != 0)
- mmc_sim->ccb->ccb_h.status = CAM_REQ_INVALID;
+ ccb->ccb_h.status = CAM_REQ_INVALID;
else
- mmc_sim->ccb->ccb_h.status = CAM_REQ_CMP;
+ ccb->ccb_h.status = CAM_REQ_CMP;
break;
case XPT_MMC_SET_TRAN_SETTINGS:
rv = MMC_SIM_SET_TRAN_SETTINGS(mmc_sim->dev, &cts->proto_specific.mmc);
if (rv != 0)
- mmc_sim->ccb->ccb_h.status = CAM_REQ_INVALID;
+ ccb->ccb_h.status = CAM_REQ_INVALID;
else
- mmc_sim->ccb->ccb_h.status = CAM_REQ_CMP;
+ ccb->ccb_h.status = CAM_REQ_CMP;
break;
default:
- panic("Unsupported ccb func %x\n", mmc_sim->ccb->ccb_h.func_code);
+ panic("Unsupported ccb func %x\n", ccb->ccb_h.func_code);
break;
}
- xpt_done(mmc_sim->ccb);
+ /*
+ * Release the SIM before completing the request: the requester may
+ * submit the next one as soon as it is done.
+ */
mmc_sim->ccb = NULL;
+ xpt_done(ccb);
}
@@ -133,7 +139,7 @@
{
ccb->ccb_h.status = CAM_SIM_QUEUED;
mmc_sim->ccb = ccb;
- taskqueue_enqueue(taskqueue_thread, &mmc_sim->sim_task);
+ taskqueue_enqueue(mmc_sim->tq, &mmc_sim->sim_task);
return;
/* NOTREACHED */
break;
@@ -153,7 +159,7 @@
{
ccb->ccb_h.status = CAM_SIM_QUEUED;
mmc_sim->ccb = ccb;
- taskqueue_enqueue(taskqueue_thread, &mmc_sim->sim_task);
+ taskqueue_enqueue(mmc_sim->tq, &mmc_sim->sim_task);
return;
/* NOTREACHED */
break;
@@ -193,6 +199,18 @@
snprintf(mmc_sim->name, sizeof(mmc_sim->name), "%s_sim", name);
mtx_init(&mmc_sim->mtx, mmc_sim->name, NULL, MTX_DEF);
+ /*
+ * Requests that can sleep (such as changing the bus settings, which
+ * may involve tuning) are run from a thread of our own. Don't share
+ * taskqueue_thread: it is also used by the peripheral driver to run
+ * the card initialization, which waits for these requests.
+ */
+ TASK_INIT(&mmc_sim->sim_task, 0, mmc_sim_task, mmc_sim);
+ mmc_sim->tq = taskqueue_create(mmc_sim->name, M_WAITOK,
+ taskqueue_thread_enqueue, &mmc_sim->tq);
+ taskqueue_start_threads(&mmc_sim->tq, 1, PI_DISK, "%s taskq",
+ mmc_sim->name);
+
/* Provide sim_poll hook only if the device has the poll method. */
kobj_desc = &mmc_sim_cam_poll_desc;
kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
@@ -218,7 +236,6 @@
}
mtx_unlock(&mmc_sim->mtx);
- TASK_INIT(&mmc_sim->sim_task, 0, mmc_sim_task, mmc_sim);
return (0);
@@ -231,6 +248,16 @@
mmc_cam_sim_free(struct mmc_sim *mmc_sim)
{
+ /*
+ * Quiesce the thread first: a pending settings request completes
+ * through the SIM, so the bus must still be registered.
+ */
+ if (mmc_sim->tq != NULL) {
+ taskqueue_drain(mmc_sim->tq, &mmc_sim->sim_task);
+ taskqueue_free(mmc_sim->tq);
+ mmc_sim->tq = NULL;
+ }
+
if (mmc_sim->sim != NULL) {
mtx_lock(&mmc_sim->mtx);
xpt_bus_deregister(cam_sim_path(mmc_sim->sim));
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 14, 8:40 PM (3 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41718602
Default Alt Text
D60884.1794688836.diff (11 KB)
Attached To
Mode
D60884: mmccam: Add HS200 support for eMMC
Attached
Detach File
Event Timeline