From: "Philippe Mathieu-Daudé" <f4bug@amsat.org>
To: Peter Maydell <peter.maydell@linaro.org>,
"Edgar E . Iglesias" <edgar.iglesias@xilinx.com>,
Alistair Francis <alistair@alistair23.me>
Cc: "Philippe Mathieu-Daudé" <f4bug@amsat.org>, qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH v3 1/1] sdcard: Implement the UHS-I Switch Function entries (Spec v3)
Date: Wed, 6 Jun 2018 12:48:35 -0300 [thread overview]
Message-ID: <20180606154835.2276-2-f4bug@amsat.org> (raw)
In-Reply-To: <20180606154835.2276-1-f4bug@amsat.org>
Per the "Physical Layer Simplified Specification Version 3.01":
2. System Features
Switch function command supports Bus Speed Mode, Command
System, Drive Strength, and future functions.
4.3.10 Switch Function Command
The switch function command (CMD6) is used to switch or
expand memory card functions.
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
hw/sd/sd.c | 238 ++++++++++++++++++++++++++++++++++++++++-----
hw/sd/trace-events | 1 +
2 files changed, 217 insertions(+), 22 deletions(-)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 7af19fa06c..5b05d77ac0 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -124,6 +124,10 @@ struct SDState {
bool enable;
uint8_t dat_lines;
bool cmd_line;
+ /* Whether the card entered the UHS mode with voltage level adjusted
+ * (used by soft-reset)
+ */
+ bool uhs_activated;
};
static const char *sd_state_name(enum SDCardStates state)
@@ -561,6 +565,7 @@ static void sd_reset(DeviceState *dev)
sd->blk_len = 0x200;
sd->pwd_len = 0;
sd->expecting_acmd = false;
+ sd->uhs_activated = false;
sd->dat_lines = 0xf;
sd->cmd_line = true;
sd->multi_blk_cnt = 0;
@@ -761,32 +766,221 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr)
return ret;
}
+/* Function Group */
+enum {
+ FG_MIN = 1,
+ FG_ACCESS_MODE = 1,
+ FG_COMMAND_SYSTEM = 2,
+ FG_DRIVER_STRENGTH = 3,
+ FG_CURRENT_LIMIT = 4,
+ FG_RSVD_5 = 5,
+ FG_RSVD_6 = 6,
+ FG_MAX = 6,
+ FG_COUNT
+};
+
+/* Function name */
+enum {
+ FN_NO_INFLUENCE = 0xf,
+ FN_COUNT = 16
+};
+
+/* Bus Speed Mode */
+static bool fg1_selectable(SDState *sd, int fn)
+{
+ if (sd->uhs_activated) {
+ return fn <= 4;
+ }
+ return fn <= 1;
+}
+
+/* driver strength selection for UHS-I modes */
+static bool fg3_selectable(SDState *sd, int fn)
+{
+ if (sd->uhs_activated) {
+ return fn <= 3;
+ }
+ return false;
+}
+
+/* Current Limit switch for SDR50, SDR104 and DDR50 */
+static bool fg4_selectable(SDState *sd, int fn)
+{
+ if (sd->uhs_activated) {
+ return fn <= 3;
+ }
+ return false;
+}
+
+typedef struct sd_fn_support {
+ bool (*is_selectable)(SDState *sd, int fn);
+ const char *name[FN_COUNT];
+} sd_fn_support;
+
+static const sd_fn_support fn_support_defs[FG_COUNT] = {
+ [FG_ACCESS_MODE] = {
+ &fg1_selectable, {
+ [0] = "default/SDR12",
+ [1] = "high-speed/SDR25",
+ [2] = "SDR50",
+ [3] = "SDR104",
+ [4] = "DDR50",
+ }
+ },
+ [FG_COMMAND_SYSTEM] = {
+ NULL, {
+ [1] = "For eC",
+ [3] = "OTP",
+ [4] = "ASSD",
+ }
+ },
+ [FG_DRIVER_STRENGTH] = {
+ &fg3_selectable, {
+ [0] = "default/Type B",
+ [1] = "Type A",
+ [2] = "Type C",
+ [3] = "Type D",
+ }
+ },
+ [FG_CURRENT_LIMIT] = {
+ &fg4_selectable, {
+ [0] = "default/200mA",
+ [1] = "400mA",
+ [2] = "600mA",
+ [3] = "800mA",
+ }
+ },
+};
+
+static const char *sd_fn_grp_name[FG_COUNT] = {
+ [FG_ACCESS_MODE] = "ACCESS_MODE",
+ [FG_COMMAND_SYSTEM] = "COMMAND_SYSTEM",
+ [FG_DRIVER_STRENGTH] = "DRIVER_STRENGTH",
+ [FG_CURRENT_LIMIT] = "CURRENT_LIMIT",
+ [FG_RSVD_5] = "RSVD5",
+ [FG_RSVD_6] = "RSVD6",
+};
+
+static const char *sd_fn_name(int grp, int fn)
+{
+ if (fn_support_defs[grp].name[fn]) {
+ return fn_support_defs[grp].name[fn];
+ }
+ if (fn == 0) {
+ return "default";
+ }
+ return "invalid";
+};
+
+static bool sd_function_is_selectable(SDState *sd, int grp, int fn)
+{
+ const sd_fn_support *def = &fn_support_defs[grp];
+
+ if (fn == 0) {
+ /* default is always selectable */
+ return true;
+ }
+ if (def->is_selectable) {
+ return def->is_selectable(sd, fn);
+ }
+ return false;
+}
+
+static bool sd_function_is_valid(SDState *sd, int group, int fn)
+{
+ const sd_fn_support *def = &fn_support_defs[group];
+ bool is_valid = false;
+
+ if (fn == FN_NO_INFLUENCE) {
+ fn = sd->function_group[group - 1];
+ }
+ if (fn == 0) {
+ /* default is always valid */
+ is_valid = true;
+ } else if (!def->name[fn]) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Function %d not valid for function group %d\n",
+ fn, group);
+ } else if (!def->is_selectable) {
+ qemu_log_mask(LOG_UNIMP, "Function %s (fn group %d) not implemented\n",
+ sd_fn_name(group, fn), group);
+ } else if (!def->is_selectable(sd, fn)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Function %s (fn group %d) only valid in current mode\n",
+ sd_fn_name(group, fn), group);
+ } else {
+ is_valid = true;
+ }
+
+ return is_valid;
+}
+
static void sd_function_switch(SDState *sd, uint32_t arg)
{
- int i, mode, new_func;
- mode = !!(arg & 0x80000000);
-
- sd->data[0] = 0x00; /* Maximum current consumption */
- sd->data[1] = 0x01;
- sd->data[2] = 0x80; /* Supported group 6 functions */
- sd->data[3] = 0x01;
- sd->data[4] = 0x80; /* Supported group 5 functions */
- sd->data[5] = 0x01;
- sd->data[6] = 0x80; /* Supported group 4 functions */
- sd->data[7] = 0x01;
- sd->data[8] = 0x80; /* Supported group 3 functions */
- sd->data[9] = 0x01;
- sd->data[10] = 0x80; /* Supported group 2 functions */
- sd->data[11] = 0x43;
- sd->data[12] = 0x80; /* Supported group 1 functions */
- sd->data[13] = 0x03;
- for (i = 0; i < 6; i ++) {
- new_func = (arg >> (i * 4)) & 0x0f;
- if (mode && new_func != 0x0f)
- sd->function_group[i] = new_func;
- sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4);
+ int group, func_num, i;
+ uint16_t max_current_mA = 1; /* 1 mA is virtually enough */
+ uint32_t switch_fns = 0;
+ bool do_switch = extract32(arg, 31, 1);
+
+ /* Bits 400-495: groups functions supported (16 bits each group) */
+ for (group = FG_MIN; group <= FG_MAX; group++) {
+ uint16_t supported_fns = 1 << FN_NO_INFLUENCE;
+
+ for (i = 0; i < FN_COUNT; ++i) {
+ if (sd_function_is_selectable(sd, group, i)) {
+ supported_fns |= 1 << i;
+ }
+ }
+ stw_be_p(sd->data + 2 + 2 * (FG_MAX - group), supported_fns);
+ }
+
+ /* Bits 376-399: functions information (4 bits each group) */
+ for (group = FG_MIN; group <= FG_MAX; group++) {
+ bool fn_valid;
+ func_num = (arg >> ((group - 1) * 4)) & 0x0f;
+ fn_valid = sd_function_is_valid(sd, group, func_num);
+ trace_sdcard_function_select(1, "valid", fn_valid,
+ group, sd_fn_grp_name[group],
+ sd_fn_name(group, func_num));
+ if (!fn_valid) {
+ /* 0xf shows function set error with the argument. */
+ switch_fns = deposit32(switch_fns, 4 * (group + 1), 4, 0xf);
+ }
+ }
+ if (switch_fns) {
+ /* pass #1 set switch_fns if an invalid function got selected.
+ *
+ * Maximum current consumption:
+ * If one of the selected functions was wrong, the return value is 0.
+ */
+ max_current_mA = 0;
+ } else {
+ for (group = FG_MIN; group <= FG_MAX; group++) {
+ func_num = (arg >> ((group - 1) * 4)) & 0x0f;
+ if (func_num == FN_NO_INFLUENCE) {
+ /* Guest requested no influence, so this function group
+ * stays the same.
+ */
+ func_num = sd->function_group[group - 1];
+ }
+ trace_sdcard_function_select(2, "switch", do_switch,
+ group, sd_fn_grp_name[group],
+ sd_fn_name(group, func_num));
+ if (do_switch) {
+ sd->function_group[group - 1] = func_num;
+ }
+ switch_fns = deposit32(switch_fns, 4 * (group + 1), 4, func_num);
+ }
}
+ stl_be_p(sd->data + 14, switch_fns);
+
+ /* Data Structure Version = 0x00: 376 bits reserved (undefined) */
memset(&sd->data[17], 0, 47);
+
+ /* Bits 496-511 Maximum current consumption */
+ stw_be_p(sd->data, max_current_mA);
+
+ /* Finally update the checksum */
stw_be_p(sd->data + 64, sd_crc16(sd->data, 64));
}
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index bfd1d62efc..82fa2e18d4 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -48,6 +48,7 @@ sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d"
sdcard_set_voltage(uint16_t millivolts) "%u mV"
+sdcard_function_select(int pass, const char *bool_name, bool bool_value, int grp_num, const char *grp_name, const char *fn_name) "PASS#%d %s:%u group:%d/%s func:'%s'"
# hw/sd/milkymist-memcard.c
milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
--
2.17.1
next prev parent reply other threads:[~2018-06-06 15:48 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-06 15:48 [Qemu-devel] [RFC PATCH v3 0/1] SDCard: support UHS-I Philippe Mathieu-Daudé
2018-06-06 15:48 ` Philippe Mathieu-Daudé [this message]
2018-06-15 11:00 ` Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180606154835.2276-2-f4bug@amsat.org \
--to=f4bug@amsat.org \
--cc=alistair@alistair23.me \
--cc=edgar.iglesias@xilinx.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).