Message ID | 003401ceda2a$bd04e7e0$370eb7a0$%jun@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Seungwon, On 5 November 2013 14:27, Seungwon Jeon <tgih.jun@samsung.com> wrote: > Current implementation for bus speed mode selection is too > complicated. This patch is to simplify the codes and remove > some duplicate parts. > > The following changes are including: > * Adds functions for each mode selection(HS, HS-DDR, HS200 and etc) > * Rearranged the mode selection sequence with supported device type > * Power class is switched once only after bus modes(speed/width) > are selected finally > * Adds maximum speed for HS200 mode(hs200_max_dtr) > * Adds available device type to be supported by both host and device > * Adds field definition for HS_TIMING of EXT_CSD Very nice work you are doing here, this is certainly needed. Although, from a reviewing point of view it would be nice if you could split up and do re-factoring in more minor pieces. Could that be done? Kind regards Ulf Hansson > > Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> > --- > drivers/mmc/core/mmc.c | 574 +++++++++++++++++++++++++++------------------- > include/linux/mmc/card.h | 6 +- > include/linux/mmc/host.h | 6 - > include/linux/mmc/mmc.h | 4 + > 4 files changed, 345 insertions(+), 245 deletions(-) > > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c > index f631f5a..f4f8991 100644 > --- a/drivers/mmc/core/mmc.c > +++ b/drivers/mmc/core/mmc.c > @@ -242,29 +242,48 @@ static void mmc_select_card_type(struct mmc_card *card) > struct mmc_host *host = card->host; > u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; > u32 caps = host->caps, caps2 = host->caps2; > - unsigned int hs_max_dtr = 0; > + unsigned int avail_type = 0; > + unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; > > - if (card_type & EXT_CSD_CARD_TYPE_26) > + if (card_type & EXT_CSD_CARD_TYPE_26) { > hs_max_dtr = MMC_HIGH_26_MAX_DTR; > + avail_type |= EXT_CSD_CARD_TYPE_26; > + } > > if (caps & MMC_CAP_MMC_HIGHSPEED && > - card_type & EXT_CSD_CARD_TYPE_52) > + card_type & EXT_CSD_CARD_TYPE_52) { > hs_max_dtr = MMC_HIGH_52_MAX_DTR; > + avail_type |= EXT_CSD_CARD_TYPE_52; > + } > + > + if (caps & MMC_CAP_1_8V_DDR && > + card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { > + hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; > + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; > + } > > - if ((caps & MMC_CAP_1_8V_DDR && > - card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) || > - (caps & MMC_CAP_1_2V_DDR && > - card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)) > + if (caps & MMC_CAP_1_2V_DDR && > + card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { > hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; > + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; > + } > + > + if (caps2 & MMC_CAP2_HS200_1_8V_SDR && > + card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) { > + hs200_max_dtr = MMC_HS200_MAX_DTR; > + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_8V; > + } > > - if ((caps2 & MMC_CAP2_HS200_1_8V_SDR && > - card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) || > - (caps2 & MMC_CAP2_HS200_1_2V_SDR && > - card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)) > - hs_max_dtr = MMC_HS200_MAX_DTR; > + if (caps2 & MMC_CAP2_HS200_1_2V_SDR && > + card_type & EXT_CSD_CARD_TYPE_SDR_1_2V) { > + hs200_max_dtr = MMC_HS200_MAX_DTR; > + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_2V; > + } > > card->ext_csd.hs_max_dtr = hs_max_dtr; > + card->ext_csd.hs200_max_dtr = hs200_max_dtr; > card->ext_csd.card_type = card_type; > + card->mmc_avail_type = avail_type; > } > > /* > @@ -714,8 +733,8 @@ static struct device_type mmc_type = { > * extended CSD register, select it by executing the > * mmc_switch command. > */ > -static int mmc_select_powerclass(struct mmc_card *card, > - unsigned int bus_width) > +static int __mmc_select_powerclass(struct mmc_card *card, > + unsigned int bus_width, u8 *ext_csd) > { > int err = 0; > unsigned int pwrclass_val = 0; > @@ -787,40 +806,99 @@ static int mmc_select_powerclass(struct mmc_card *card, > return err; > } > > +static inline unsigned int mmc_snoop_ddr(struct mmc_card *card) > +{ > + return card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; > +} > + > +static int mmc_select_powerclass(struct mmc_card *card, u8 *ext_csd) > +{ > + int err, ddr; > + u32 bus_width, ext_csd_bits; > + struct mmc_host *host; > + > + BUG_ON(!card); > + > + host = card->host; > + > + if (!ext_csd) > + return 0; > + > + /* Power class selection is supported for versions >= 4.0 */ > + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) > + return 0; > + > + bus_width = host->ios.bus_width; > + /* Power class values are defined only for 4/8 bit bus */ > + if (bus_width == MMC_BUS_WIDTH_1) > + return 0; > + > + ddr = mmc_snoop_ddr(card); > + if (ddr) > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; > + else > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > + EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; > + > + err = __mmc_select_powerclass(card, ext_csd_bits, ext_csd); > + if (err) > + pr_warn("%s: power class selection to bus width %d ddr %d failed\n", > + mmc_hostname(host), 1 << bus_width, ddr); > + > + return err; > +} > + > /* > - * Selects the desired buswidth and switch to the HS200 mode > - * if bus width set without error > + * Set the bus speed for the selected speed mode. > */ > -static int mmc_select_hs200(struct mmc_card *card) > +static void mmc_set_bus_speed(struct mmc_card *card) > +{ > + unsigned int max_dtr = (unsigned int)-1; > + > + BUG_ON(!card); > + > + if (mmc_card_hs200(card)) { > + if (max_dtr > card->ext_csd.hs200_max_dtr) > + max_dtr = card->ext_csd.hs200_max_dtr; > + } else if (mmc_card_highspeed(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) { > + max_dtr = card->csd.max_dtr; > + } > + > + mmc_set_clock(card->host, max_dtr); > +} > + > +/* > + * Select the bus width amoung 4-bit and 8-bit(SDR). > + * If the bus width is changed successfully, return the slected width value. > + * Zero is returned instead of error value if the wide width is not supported. > + */ > +static int mmc_select_bus_width(struct mmc_card *card) > { > - int idx, err = -EINVAL; > - struct mmc_host *host; > static unsigned ext_csd_bits[] = { > - EXT_CSD_BUS_WIDTH_4, > EXT_CSD_BUS_WIDTH_8, > + EXT_CSD_BUS_WIDTH_4, > }; > static unsigned bus_widths[] = { > - MMC_BUS_WIDTH_4, > MMC_BUS_WIDTH_8, > + MMC_BUS_WIDTH_4, > }; > + struct mmc_host *host; > + unsigned idx, bus_width = 0; > + int err = 0; > > BUG_ON(!card); > > host = card->host; > > - if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && > - host->caps2 & MMC_CAP2_HS200_1_2V_SDR) > - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); > - > - if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && > - host->caps2 & MMC_CAP2_HS200_1_8V_SDR) > - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); > - > - /* If fails try again during next card power cycle */ > - if (err) > - goto err; > + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && > + !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) > + return 0; > > - idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; > + idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1; > > /* > * Unlike SD, MMC cards dont have a configuration register to notify > @@ -828,8 +906,7 @@ static int mmc_select_hs200(struct mmc_card *card) > * the supported bus width or compare the ext csd values of current > * bus width and ext csd values of 1 bit mode read earlier. > */ > - for (; idx >= 0; idx--) { > - > + for (; idx < ARRAY_SIZE(bus_widths); idx++) { > /* > * Host is capable of 8bit transfer, then switch > * the device to work in 8bit transfer mode. If the > @@ -844,25 +921,226 @@ static int mmc_select_hs200(struct mmc_card *card) > if (err) > continue; > > - mmc_set_bus_width(card->host, bus_widths[idx]); > + bus_width = bus_widths[idx]; > + mmc_set_bus_width(host, bus_width); > > + /* > + * If controller can't handle bus width test, > + * compare ext_csd previously read in 1 bit mode > + * against ext_csd at new bus width > + */ > if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) > - err = mmc_compare_ext_csds(card, bus_widths[idx]); > + err = mmc_compare_ext_csds(card, bus_width); > else > - err = mmc_bus_test(card, bus_widths[idx]); > - if (!err) > + err = mmc_bus_test(card, bus_width); > + > + if (!err) { > + err = bus_width; > break; > + } else { > + pr_warn("%s: switch to bus width %d failed\n", > + mmc_hostname(card->host), ext_csd_bits[idx]); > + } > } > > - /* switch to HS200 mode if bus width set successfully */ > - if (!err) > + return err; > +} > + > +/* > + * Switch to the high-speed mode > + */ > +static int mmc_select_hs(struct mmc_card *card) > +{ > + int err; > + > + BUG_ON(!card); > + > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, > + card->ext_csd.generic_cmd6_time); > + if (!err) { > + mmc_card_set_highspeed(card); > + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); > + } > + > + return err; > +} > + > +/* > + * Activate wide bus and DDR if supported. > + */ > +static int mmc_select_hs_ddr(struct mmc_card *card, u8 *ext_csd) > +{ > + struct mmc_host *host; > + u32 bus_width, ext_csd_bits; > + int err = 0, ddr; > + > + BUG_ON(!card); > + > + ddr = mmc_snoop_ddr(card); > + if (!(ddr & EXT_CSD_CARD_TYPE_DDR_52)) > + return 0; > + > + host = card->host; > + bus_width = host->ios.bus_width; > + if (bus_width == MMC_BUS_WIDTH_1) > + return 0; > + > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; > + > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_BUS_WIDTH, > + ext_csd_bits, > + card->ext_csd.generic_cmd6_time); > + if (err) { > + pr_warn("%s: switch to bus width %d ddr failed\n", > + mmc_hostname(host), 1 << bus_width); > + return err; > + } > + > + /* > + * eMMC cards can support 3.3V to 1.2V i/o (vccq) > + * signaling. > + * > + * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. > + * > + * 1.8V vccq at 3.3V core voltage (vcc) is not required > + * in the JEDEC spec for DDR. > + * > + * Do not force change in vccq since we are obviously > + * working and no change to vccq is needed. > + * > + * WARNING: eMMC rules are NOT the same as SD DDR > + */ > + if (ddr & EXT_CSD_CARD_TYPE_DDR_1_2V) { > + err = __mmc_set_signal_voltage(host, > + MMC_SIGNAL_VOLTAGE_120); > + if (err) > + return err; > + } > + > + mmc_card_set_ddr_mode(card); > + mmc_set_timing(host, MMC_TIMING_UHS_DDR50); > + > + return err; > +} > + > +/* > + * For device supporting HS200 mode, the following sequence > + * should be done before executing the tuning process. > + * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported) > + * 2. switch to HS200 mode > + * 3. set the clock to > 52Mhz and <=200MHz > + */ > +static int mmc_select_hs200(struct mmc_card *card) > +{ > + int err = -EINVAL; > + struct mmc_host *host; > + > + BUG_ON(!card); > + > + host = card->host; > + > + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_2V) > + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); > + > + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_8V) > + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); > + > + /* If fails try again during next card power cycle */ > + if (err) > + goto err; > + > + /* > + * Set the bus width(4 or 8) with host's support and > + * switch to HS200 mode if bus width is set successfully. > + */ > + err = mmc_select_bus_width(card); > + if (!IS_ERR_VALUE(err)) { > err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > - EXT_CSD_HS_TIMING, 2, 0); > + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, > + card->ext_csd.generic_cmd6_time); > + if (!err) { > + mmc_card_set_hs200(card); > + mmc_set_timing(host, MMC_TIMING_MMC_HS200); > + } > + } > err: > return err; > } > > /* > + * Activate High Speed or HS200 mode if supported. > + */ > +static int mmc_select_timing(struct mmc_card *card) > +{ > + struct mmc_host *host; > + int err = 0; > + > + BUG_ON(!card); > + > + host = card->host; > + > + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && > + card->ext_csd.hs_max_dtr == 0)) > + goto bus_speed; > + > + if (card->ext_csd.hs200_max_dtr > 52000000 && > + host->caps2 & MMC_CAP2_HS200) > + err = mmc_select_hs200(card); > + else if (host->caps & MMC_CAP_MMC_HIGHSPEED) > + err = mmc_select_hs(card); > + > + if (err && err != -EBADMSG) > + return err; > + > + if (err) { > + pr_warn("%s: switch to %s failed\n", > + mmc_card_highspeed(card) ? "high-speed" : > + (mmc_card_hs200(card) ? "hs200" : ""), > + mmc_hostname(card->host)); > + err = 0; > + } > + > +bus_speed: > + /* > + * Set the bus speed to the selected bus timing. > + * If timing is not selected, backward compatible is the default. > + */ > + mmc_set_bus_speed(card); > + return err; > +} > + > +/* > + * Execute tuning sequence to seek the proper bus operating > + * conditions for HS200, which sends CMD21 to the device. > + */ > +static int mmc_hs200_tuning(struct mmc_card *card) > +{ > + int err = 0; > + struct mmc_host *host; > + > + BUG_ON(!card); > + > + host = card->host; > + > + if (host->caps2 & MMC_CAP2_HS200 && > + card->host->ops->execute_tuning) { > + mmc_host_clk_hold(card->host); > + err = card->host->ops->execute_tuning(card->host, > + MMC_SEND_TUNING_BLOCK_HS200); > + mmc_host_clk_release(card->host); > + > + if (err) > + pr_warn("%s: tuning execution failed\n", > + mmc_hostname(card->host)); > + } > + > + return err; > +} > + > +/* > * Handle the detection and initialisation of a card. > * > * In the case of a resume, "oldcard" will contain the card > @@ -872,9 +1150,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, > struct mmc_card *oldcard) > { > struct mmc_card *card; > - int err, ddr = 0; > + int err; > u32 cid[4]; > - unsigned int max_dtr; > u32 rocr; > u8 *ext_csd = NULL; > > @@ -1066,209 +1343,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, > } > > /* > - * Activate high speed (if supported) > - */ > - if (card->ext_csd.hs_max_dtr != 0) { > - err = 0; > - if (card->ext_csd.hs_max_dtr > 52000000 && > - host->caps2 & MMC_CAP2_HS200) > - err = mmc_select_hs200(card); > - else if (host->caps & MMC_CAP_MMC_HIGHSPEED) > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > - EXT_CSD_HS_TIMING, 1, > - card->ext_csd.generic_cmd6_time); > - > - if (err && err != -EBADMSG) > - goto free_card; > - > - if (err) { > - pr_warning("%s: switch to highspeed failed\n", > - mmc_hostname(card->host)); > - err = 0; > - } else { > - if (card->ext_csd.hs_max_dtr > 52000000 && > - host->caps2 & MMC_CAP2_HS200) { > - mmc_card_set_hs200(card); > - mmc_set_timing(card->host, > - MMC_TIMING_MMC_HS200); > - } else { > - mmc_card_set_highspeed(card); > - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); > - } > - } > - } > - > - /* > - * Compute bus speed. > - */ > - max_dtr = (unsigned int)-1; > - > - if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { > - if (max_dtr > card->ext_csd.hs_max_dtr) > - max_dtr = card->ext_csd.hs_max_dtr; > - if (mmc_card_highspeed(card) && (max_dtr > 52000000)) > - max_dtr = 52000000; > - } else if (max_dtr > card->csd.max_dtr) { > - max_dtr = card->csd.max_dtr; > - } > - > - mmc_set_clock(host, max_dtr); > - > - /* > - * Indicate DDR mode (if supported). > + * Select timing interface > */ > - if (mmc_card_highspeed(card)) { > - if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) > - && ((host->caps & (MMC_CAP_1_8V_DDR | > - MMC_CAP_UHS_DDR50)) > - == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) > - ddr = MMC_1_8V_DDR_MODE; > - else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) > - && ((host->caps & (MMC_CAP_1_2V_DDR | > - MMC_CAP_UHS_DDR50)) > - == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) > - ddr = MMC_1_2V_DDR_MODE; > - } > + err = mmc_select_timing(card); > + if (err) > + goto free_card; > > - /* > - * Indicate HS200 SDR mode (if supported). > - */ > if (mmc_card_hs200(card)) { > - u32 ext_csd_bits; > - u32 bus_width = card->host->ios.bus_width; > - > - /* > - * For devices supporting HS200 mode, the bus width has > - * to be set before executing the tuning function. If > - * set before tuning, then device will respond with CRC > - * errors for responses on CMD line. So for HS200 the > - * sequence will be > - * 1. set bus width 4bit / 8 bit (1 bit not supported) > - * 2. switch to HS200 mode > - * 3. set the clock to > 52Mhz <=200MHz and > - * 4. execute tuning for HS200 > - */ > - if ((host->caps2 & MMC_CAP2_HS200) && > - card->host->ops->execute_tuning) { > - mmc_host_clk_hold(card->host); > - err = card->host->ops->execute_tuning(card->host, > - MMC_SEND_TUNING_BLOCK_HS200); > - mmc_host_clk_release(card->host); > - } > - if (err) { > - pr_warning("%s: tuning execution failed\n", > - mmc_hostname(card->host)); > + err = mmc_hs200_tuning(card); > + if (err) > goto err; > + } else if (mmc_card_highspeed(card)) { > + /* Select the desired bus width optionally */ > + err = mmc_select_bus_width(card); > + if (!IS_ERR_VALUE(err)) { > + err = mmc_select_hs_ddr(card, ext_csd); > + if (err) > + goto err; > } > - > - ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > - EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; > - err = mmc_select_powerclass(card, ext_csd_bits); > - if (err) > - pr_warning("%s: power class selection to bus width %d" > - " failed\n", mmc_hostname(card->host), > - 1 << bus_width); > } > > /* > - * Activate wide bus and DDR (if supported). > + * Choose the power calss with selected bus interface > */ > - if (!mmc_card_hs200(card) && > - (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && > - (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { > - static unsigned ext_csd_bits[][2] = { > - { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, > - { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, > - { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, > - }; > - static unsigned bus_widths[] = { > - MMC_BUS_WIDTH_8, > - MMC_BUS_WIDTH_4, > - MMC_BUS_WIDTH_1 > - }; > - unsigned idx, bus_width = 0; > - > - if (host->caps & MMC_CAP_8_BIT_DATA) > - idx = 0; > - else > - idx = 1; > - for (; idx < ARRAY_SIZE(bus_widths); idx++) { > - bus_width = bus_widths[idx]; > - if (bus_width == MMC_BUS_WIDTH_1) > - ddr = 0; /* no DDR for 1-bit width */ > - err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); > - if (err) > - pr_warning("%s: power class selection to " > - "bus width %d failed\n", > - mmc_hostname(card->host), > - 1 << bus_width); > - > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > - EXT_CSD_BUS_WIDTH, > - ext_csd_bits[idx][0], > - card->ext_csd.generic_cmd6_time); > - if (!err) { > - mmc_set_bus_width(card->host, bus_width); > - > - /* > - * If controller can't handle bus width test, > - * compare ext_csd previously read in 1 bit mode > - * against ext_csd at new bus width > - */ > - if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) > - err = mmc_compare_ext_csds(card, > - bus_width); > - else > - err = mmc_bus_test(card, bus_width); > - if (!err) > - break; > - } > - } > - > - if (!err && ddr) { > - err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); > - if (err) > - pr_warning("%s: power class selection to " > - "bus width %d ddr %d failed\n", > - mmc_hostname(card->host), > - 1 << bus_width, ddr); > - > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > - EXT_CSD_BUS_WIDTH, > - ext_csd_bits[idx][1], > - card->ext_csd.generic_cmd6_time); > - } > - if (err) { > - pr_warning("%s: switch to bus width %d ddr %d " > - "failed\n", mmc_hostname(card->host), > - 1 << bus_width, ddr); > - goto free_card; > - } else if (ddr) { > - /* > - * eMMC cards can support 3.3V to 1.2V i/o (vccq) > - * signaling. > - * > - * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. > - * > - * 1.8V vccq at 3.3V core voltage (vcc) is not required > - * in the JEDEC spec for DDR. > - * > - * Do not force change in vccq since we are obviously > - * working and no change to vccq is needed. > - * > - * WARNING: eMMC rules are NOT the same as SD DDR > - */ > - if (ddr == MMC_1_2V_DDR_MODE) { > - err = __mmc_set_signal_voltage(host, > - MMC_SIGNAL_VOLTAGE_120); > - if (err) > - goto err; > - } > - mmc_card_set_ddr_mode(card); > - mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); > - mmc_set_bus_width(card->host, bus_width); > - } > - } > + mmc_select_powerclass(card, ext_csd); > > /* > * Enable HPI feature (if supported) > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h > index 176fdf8..c119735 100644 > --- a/include/linux/mmc/card.h > +++ b/include/linux/mmc/card.h > @@ -63,6 +63,7 @@ struct mmc_ext_csd { > unsigned int power_off_longtime; /* Units: ms */ > u8 power_off_notification; /* state */ > unsigned int hs_max_dtr; > + unsigned int hs200_max_dtr; > #define MMC_HIGH_26_MAX_DTR 26000000 > #define MMC_HIGH_52_MAX_DTR 52000000 > #define MMC_HIGH_DDR_MAX_DTR 52000000 > @@ -299,7 +300,10 @@ struct mmc_card { > const char **info; /* info strings */ > struct sdio_func_tuple *tuples; /* unknown common tuples */ > > - unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ > + union { > + unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ > + unsigned int mmc_avail_type; /* supported device type by both host and card */ > + }; > > struct dentry *debugfs_root; > struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h > index 99f5709..69d58b1 100644 > --- a/include/linux/mmc/host.h > +++ b/include/linux/mmc/host.h > @@ -60,12 +60,6 @@ struct mmc_ios { > #define MMC_TIMING_UHS_DDR50 7 > #define MMC_TIMING_MMC_HS200 8 > > -#define MMC_SDR_MODE 0 > -#define MMC_1_2V_DDR_MODE 1 > -#define MMC_1_8V_DDR_MODE 2 > -#define MMC_1_2V_SDR_MODE 3 > -#define MMC_1_8V_SDR_MODE 4 > - > unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ > > #define MMC_SIGNAL_VOLTAGE_330 0 > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h > index 50bcde3..87df508 100644 > --- a/include/linux/mmc/mmc.h > +++ b/include/linux/mmc/mmc.h > @@ -373,6 +373,10 @@ struct _mmc_csd { > #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ > #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ > > +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ > +#define EXT_CSD_TIMING_HS 1 /* High speed */ > +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ > + > #define EXT_CSD_SEC_ER_EN BIT(0) > #define EXT_CSD_SEC_BD_BLK_EN BIT(2) > #define EXT_CSD_SEC_GB_CL_EN BIT(4) > -- > 1.7.0.4 > > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Ulf, On Tue, November 05, 2013, Ulf Hansson wrote: > Hi Seungwon, > > On 5 November 2013 14:27, Seungwon Jeon <tgih.jun@samsung.com> wrote: > > Current implementation for bus speed mode selection is too > > complicated. This patch is to simplify the codes and remove > > some duplicate parts. > > > > The following changes are including: > > * Adds functions for each mode selection(HS, HS-DDR, HS200 and etc) > > * Rearranged the mode selection sequence with supported device type > > * Power class is switched once only after bus modes(speed/width) > > are selected finally > > * Adds maximum speed for HS200 mode(hs200_max_dtr) > > * Adds available device type to be supported by both host and device > > * Adds field definition for HS_TIMING of EXT_CSD > > Very nice work you are doing here, this is certainly needed. > > Although, from a reviewing point of view it would be nice if you could > split up and do re-factoring in more minor pieces. Could that be done? Ok. I'll consider that. But I guess it would not be done nicely because a whole flow is organic. If you have any opinion, please let me know. Thanks, Seungwon Jeon > > Kind regards > Ulf Hansson > > > > > Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> > > --- > > drivers/mmc/core/mmc.c | 574 +++++++++++++++++++++++++++------------------- > > include/linux/mmc/card.h | 6 +- > > include/linux/mmc/host.h | 6 - > > include/linux/mmc/mmc.h | 4 + > > 4 files changed, 345 insertions(+), 245 deletions(-) > > > > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c > > index f631f5a..f4f8991 100644 > > --- a/drivers/mmc/core/mmc.c > > +++ b/drivers/mmc/core/mmc.c > > @@ -242,29 +242,48 @@ static void mmc_select_card_type(struct mmc_card *card) > > struct mmc_host *host = card->host; > > u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; > > u32 caps = host->caps, caps2 = host->caps2; > > - unsigned int hs_max_dtr = 0; > > + unsigned int avail_type = 0; > > + unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; > > > > - if (card_type & EXT_CSD_CARD_TYPE_26) > > + if (card_type & EXT_CSD_CARD_TYPE_26) { > > hs_max_dtr = MMC_HIGH_26_MAX_DTR; > > + avail_type |= EXT_CSD_CARD_TYPE_26; > > + } > > > > if (caps & MMC_CAP_MMC_HIGHSPEED && > > - card_type & EXT_CSD_CARD_TYPE_52) > > + card_type & EXT_CSD_CARD_TYPE_52) { > > hs_max_dtr = MMC_HIGH_52_MAX_DTR; > > + avail_type |= EXT_CSD_CARD_TYPE_52; > > + } > > + > > + if (caps & MMC_CAP_1_8V_DDR && > > + card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { > > + hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; > > + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; > > + } > > > > - if ((caps & MMC_CAP_1_8V_DDR && > > - card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) || > > - (caps & MMC_CAP_1_2V_DDR && > > - card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)) > > + if (caps & MMC_CAP_1_2V_DDR && > > + card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { > > hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; > > + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; > > + } > > + > > + if (caps2 & MMC_CAP2_HS200_1_8V_SDR && > > + card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) { > > + hs200_max_dtr = MMC_HS200_MAX_DTR; > > + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_8V; > > + } > > > > - if ((caps2 & MMC_CAP2_HS200_1_8V_SDR && > > - card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) || > > - (caps2 & MMC_CAP2_HS200_1_2V_SDR && > > - card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)) > > - hs_max_dtr = MMC_HS200_MAX_DTR; > > + if (caps2 & MMC_CAP2_HS200_1_2V_SDR && > > + card_type & EXT_CSD_CARD_TYPE_SDR_1_2V) { > > + hs200_max_dtr = MMC_HS200_MAX_DTR; > > + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_2V; > > + } > > > > card->ext_csd.hs_max_dtr = hs_max_dtr; > > + card->ext_csd.hs200_max_dtr = hs200_max_dtr; > > card->ext_csd.card_type = card_type; > > + card->mmc_avail_type = avail_type; > > } > > > > /* > > @@ -714,8 +733,8 @@ static struct device_type mmc_type = { > > * extended CSD register, select it by executing the > > * mmc_switch command. > > */ > > -static int mmc_select_powerclass(struct mmc_card *card, > > - unsigned int bus_width) > > +static int __mmc_select_powerclass(struct mmc_card *card, > > + unsigned int bus_width, u8 *ext_csd) > > { > > int err = 0; > > unsigned int pwrclass_val = 0; > > @@ -787,40 +806,99 @@ static int mmc_select_powerclass(struct mmc_card *card, > > return err; > > } > > > > +static inline unsigned int mmc_snoop_ddr(struct mmc_card *card) > > +{ > > + return card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; > > +} > > + > > +static int mmc_select_powerclass(struct mmc_card *card, u8 *ext_csd) > > +{ > > + int err, ddr; > > + u32 bus_width, ext_csd_bits; > > + struct mmc_host *host; > > + > > + BUG_ON(!card); > > + > > + host = card->host; > > + > > + if (!ext_csd) > > + return 0; > > + > > + /* Power class selection is supported for versions >= 4.0 */ > > + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) > > + return 0; > > + > > + bus_width = host->ios.bus_width; > > + /* Power class values are defined only for 4/8 bit bus */ > > + if (bus_width == MMC_BUS_WIDTH_1) > > + return 0; > > + > > + ddr = mmc_snoop_ddr(card); > > + if (ddr) > > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > > + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; > > + else > > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > > + EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; > > + > > + err = __mmc_select_powerclass(card, ext_csd_bits, ext_csd); > > + if (err) > > + pr_warn("%s: power class selection to bus width %d ddr %d failed\n", > > + mmc_hostname(host), 1 << bus_width, ddr); > > + > > + return err; > > +} > > + > > /* > > - * Selects the desired buswidth and switch to the HS200 mode > > - * if bus width set without error > > + * Set the bus speed for the selected speed mode. > > */ > > -static int mmc_select_hs200(struct mmc_card *card) > > +static void mmc_set_bus_speed(struct mmc_card *card) > > +{ > > + unsigned int max_dtr = (unsigned int)-1; > > + > > + BUG_ON(!card); > > + > > + if (mmc_card_hs200(card)) { > > + if (max_dtr > card->ext_csd.hs200_max_dtr) > > + max_dtr = card->ext_csd.hs200_max_dtr; > > + } else if (mmc_card_highspeed(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) { > > + max_dtr = card->csd.max_dtr; > > + } > > + > > + mmc_set_clock(card->host, max_dtr); > > +} > > + > > +/* > > + * Select the bus width amoung 4-bit and 8-bit(SDR). > > + * If the bus width is changed successfully, return the slected width value. > > + * Zero is returned instead of error value if the wide width is not supported. > > + */ > > +static int mmc_select_bus_width(struct mmc_card *card) > > { > > - int idx, err = -EINVAL; > > - struct mmc_host *host; > > static unsigned ext_csd_bits[] = { > > - EXT_CSD_BUS_WIDTH_4, > > EXT_CSD_BUS_WIDTH_8, > > + EXT_CSD_BUS_WIDTH_4, > > }; > > static unsigned bus_widths[] = { > > - MMC_BUS_WIDTH_4, > > MMC_BUS_WIDTH_8, > > + MMC_BUS_WIDTH_4, > > }; > > + struct mmc_host *host; > > + unsigned idx, bus_width = 0; > > + int err = 0; > > > > BUG_ON(!card); > > > > host = card->host; > > > > - if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && > > - host->caps2 & MMC_CAP2_HS200_1_2V_SDR) > > - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); > > - > > - if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && > > - host->caps2 & MMC_CAP2_HS200_1_8V_SDR) > > - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); > > - > > - /* If fails try again during next card power cycle */ > > - if (err) > > - goto err; > > + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && > > + !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) > > + return 0; > > > > - idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; > > + idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1; > > > > /* > > * Unlike SD, MMC cards dont have a configuration register to notify > > @@ -828,8 +906,7 @@ static int mmc_select_hs200(struct mmc_card *card) > > * the supported bus width or compare the ext csd values of current > > * bus width and ext csd values of 1 bit mode read earlier. > > */ > > - for (; idx >= 0; idx--) { > > - > > + for (; idx < ARRAY_SIZE(bus_widths); idx++) { > > /* > > * Host is capable of 8bit transfer, then switch > > * the device to work in 8bit transfer mode. If the > > @@ -844,25 +921,226 @@ static int mmc_select_hs200(struct mmc_card *card) > > if (err) > > continue; > > > > - mmc_set_bus_width(card->host, bus_widths[idx]); > > + bus_width = bus_widths[idx]; > > + mmc_set_bus_width(host, bus_width); > > > > + /* > > + * If controller can't handle bus width test, > > + * compare ext_csd previously read in 1 bit mode > > + * against ext_csd at new bus width > > + */ > > if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) > > - err = mmc_compare_ext_csds(card, bus_widths[idx]); > > + err = mmc_compare_ext_csds(card, bus_width); > > else > > - err = mmc_bus_test(card, bus_widths[idx]); > > - if (!err) > > + err = mmc_bus_test(card, bus_width); > > + > > + if (!err) { > > + err = bus_width; > > break; > > + } else { > > + pr_warn("%s: switch to bus width %d failed\n", > > + mmc_hostname(card->host), ext_csd_bits[idx]); > > + } > > } > > > > - /* switch to HS200 mode if bus width set successfully */ > > - if (!err) > > + return err; > > +} > > + > > +/* > > + * Switch to the high-speed mode > > + */ > > +static int mmc_select_hs(struct mmc_card *card) > > +{ > > + int err; > > + > > + BUG_ON(!card); > > + > > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > > + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, > > + card->ext_csd.generic_cmd6_time); > > + if (!err) { > > + mmc_card_set_highspeed(card); > > + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); > > + } > > + > > + return err; > > +} > > + > > +/* > > + * Activate wide bus and DDR if supported. > > + */ > > +static int mmc_select_hs_ddr(struct mmc_card *card, u8 *ext_csd) > > +{ > > + struct mmc_host *host; > > + u32 bus_width, ext_csd_bits; > > + int err = 0, ddr; > > + > > + BUG_ON(!card); > > + > > + ddr = mmc_snoop_ddr(card); > > + if (!(ddr & EXT_CSD_CARD_TYPE_DDR_52)) > > + return 0; > > + > > + host = card->host; > > + bus_width = host->ios.bus_width; > > + if (bus_width == MMC_BUS_WIDTH_1) > > + return 0; > > + > > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > > + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; > > + > > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > > + EXT_CSD_BUS_WIDTH, > > + ext_csd_bits, > > + card->ext_csd.generic_cmd6_time); > > + if (err) { > > + pr_warn("%s: switch to bus width %d ddr failed\n", > > + mmc_hostname(host), 1 << bus_width); > > + return err; > > + } > > + > > + /* > > + * eMMC cards can support 3.3V to 1.2V i/o (vccq) > > + * signaling. > > + * > > + * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. > > + * > > + * 1.8V vccq at 3.3V core voltage (vcc) is not required > > + * in the JEDEC spec for DDR. > > + * > > + * Do not force change in vccq since we are obviously > > + * working and no change to vccq is needed. > > + * > > + * WARNING: eMMC rules are NOT the same as SD DDR > > + */ > > + if (ddr & EXT_CSD_CARD_TYPE_DDR_1_2V) { > > + err = __mmc_set_signal_voltage(host, > > + MMC_SIGNAL_VOLTAGE_120); > > + if (err) > > + return err; > > + } > > + > > + mmc_card_set_ddr_mode(card); > > + mmc_set_timing(host, MMC_TIMING_UHS_DDR50); > > + > > + return err; > > +} > > + > > +/* > > + * For device supporting HS200 mode, the following sequence > > + * should be done before executing the tuning process. > > + * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported) > > + * 2. switch to HS200 mode > > + * 3. set the clock to > 52Mhz and <=200MHz > > + */ > > +static int mmc_select_hs200(struct mmc_card *card) > > +{ > > + int err = -EINVAL; > > + struct mmc_host *host; > > + > > + BUG_ON(!card); > > + > > + host = card->host; > > + > > + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_2V) > > + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); > > + > > + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_8V) > > + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); > > + > > + /* If fails try again during next card power cycle */ > > + if (err) > > + goto err; > > + > > + /* > > + * Set the bus width(4 or 8) with host's support and > > + * switch to HS200 mode if bus width is set successfully. > > + */ > > + err = mmc_select_bus_width(card); > > + if (!IS_ERR_VALUE(err)) { > > err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > > - EXT_CSD_HS_TIMING, 2, 0); > > + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, > > + card->ext_csd.generic_cmd6_time); > > + if (!err) { > > + mmc_card_set_hs200(card); > > + mmc_set_timing(host, MMC_TIMING_MMC_HS200); > > + } > > + } > > err: > > return err; > > } > > > > /* > > + * Activate High Speed or HS200 mode if supported. > > + */ > > +static int mmc_select_timing(struct mmc_card *card) > > +{ > > + struct mmc_host *host; > > + int err = 0; > > + > > + BUG_ON(!card); > > + > > + host = card->host; > > + > > + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && > > + card->ext_csd.hs_max_dtr == 0)) > > + goto bus_speed; > > + > > + if (card->ext_csd.hs200_max_dtr > 52000000 && > > + host->caps2 & MMC_CAP2_HS200) > > + err = mmc_select_hs200(card); > > + else if (host->caps & MMC_CAP_MMC_HIGHSPEED) > > + err = mmc_select_hs(card); > > + > > + if (err && err != -EBADMSG) > > + return err; > > + > > + if (err) { > > + pr_warn("%s: switch to %s failed\n", > > + mmc_card_highspeed(card) ? "high-speed" : > > + (mmc_card_hs200(card) ? "hs200" : ""), > > + mmc_hostname(card->host)); > > + err = 0; > > + } > > + > > +bus_speed: > > + /* > > + * Set the bus speed to the selected bus timing. > > + * If timing is not selected, backward compatible is the default. > > + */ > > + mmc_set_bus_speed(card); > > + return err; > > +} > > + > > +/* > > + * Execute tuning sequence to seek the proper bus operating > > + * conditions for HS200, which sends CMD21 to the device. > > + */ > > +static int mmc_hs200_tuning(struct mmc_card *card) > > +{ > > + int err = 0; > > + struct mmc_host *host; > > + > > + BUG_ON(!card); > > + > > + host = card->host; > > + > > + if (host->caps2 & MMC_CAP2_HS200 && > > + card->host->ops->execute_tuning) { > > + mmc_host_clk_hold(card->host); > > + err = card->host->ops->execute_tuning(card->host, > > + MMC_SEND_TUNING_BLOCK_HS200); > > + mmc_host_clk_release(card->host); > > + > > + if (err) > > + pr_warn("%s: tuning execution failed\n", > > + mmc_hostname(card->host)); > > + } > > + > > + return err; > > +} > > + > > +/* > > * Handle the detection and initialisation of a card. > > * > > * In the case of a resume, "oldcard" will contain the card > > @@ -872,9 +1150,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, > > struct mmc_card *oldcard) > > { > > struct mmc_card *card; > > - int err, ddr = 0; > > + int err; > > u32 cid[4]; > > - unsigned int max_dtr; > > u32 rocr; > > u8 *ext_csd = NULL; > > > > @@ -1066,209 +1343,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, > > } > > > > /* > > - * Activate high speed (if supported) > > - */ > > - if (card->ext_csd.hs_max_dtr != 0) { > > - err = 0; > > - if (card->ext_csd.hs_max_dtr > 52000000 && > > - host->caps2 & MMC_CAP2_HS200) > > - err = mmc_select_hs200(card); > > - else if (host->caps & MMC_CAP_MMC_HIGHSPEED) > > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > > - EXT_CSD_HS_TIMING, 1, > > - card->ext_csd.generic_cmd6_time); > > - > > - if (err && err != -EBADMSG) > > - goto free_card; > > - > > - if (err) { > > - pr_warning("%s: switch to highspeed failed\n", > > - mmc_hostname(card->host)); > > - err = 0; > > - } else { > > - if (card->ext_csd.hs_max_dtr > 52000000 && > > - host->caps2 & MMC_CAP2_HS200) { > > - mmc_card_set_hs200(card); > > - mmc_set_timing(card->host, > > - MMC_TIMING_MMC_HS200); > > - } else { > > - mmc_card_set_highspeed(card); > > - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); > > - } > > - } > > - } > > - > > - /* > > - * Compute bus speed. > > - */ > > - max_dtr = (unsigned int)-1; > > - > > - if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { > > - if (max_dtr > card->ext_csd.hs_max_dtr) > > - max_dtr = card->ext_csd.hs_max_dtr; > > - if (mmc_card_highspeed(card) && (max_dtr > 52000000)) > > - max_dtr = 52000000; > > - } else if (max_dtr > card->csd.max_dtr) { > > - max_dtr = card->csd.max_dtr; > > - } > > - > > - mmc_set_clock(host, max_dtr); > > - > > - /* > > - * Indicate DDR mode (if supported). > > + * Select timing interface > > */ > > - if (mmc_card_highspeed(card)) { > > - if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) > > - && ((host->caps & (MMC_CAP_1_8V_DDR | > > - MMC_CAP_UHS_DDR50)) > > - == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) > > - ddr = MMC_1_8V_DDR_MODE; > > - else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) > > - && ((host->caps & (MMC_CAP_1_2V_DDR | > > - MMC_CAP_UHS_DDR50)) > > - == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) > > - ddr = MMC_1_2V_DDR_MODE; > > - } > > + err = mmc_select_timing(card); > > + if (err) > > + goto free_card; > > > > - /* > > - * Indicate HS200 SDR mode (if supported). > > - */ > > if (mmc_card_hs200(card)) { > > - u32 ext_csd_bits; > > - u32 bus_width = card->host->ios.bus_width; > > - > > - /* > > - * For devices supporting HS200 mode, the bus width has > > - * to be set before executing the tuning function. If > > - * set before tuning, then device will respond with CRC > > - * errors for responses on CMD line. So for HS200 the > > - * sequence will be > > - * 1. set bus width 4bit / 8 bit (1 bit not supported) > > - * 2. switch to HS200 mode > > - * 3. set the clock to > 52Mhz <=200MHz and > > - * 4. execute tuning for HS200 > > - */ > > - if ((host->caps2 & MMC_CAP2_HS200) && > > - card->host->ops->execute_tuning) { > > - mmc_host_clk_hold(card->host); > > - err = card->host->ops->execute_tuning(card->host, > > - MMC_SEND_TUNING_BLOCK_HS200); > > - mmc_host_clk_release(card->host); > > - } > > - if (err) { > > - pr_warning("%s: tuning execution failed\n", > > - mmc_hostname(card->host)); > > + err = mmc_hs200_tuning(card); > > + if (err) > > goto err; > > + } else if (mmc_card_highspeed(card)) { > > + /* Select the desired bus width optionally */ > > + err = mmc_select_bus_width(card); > > + if (!IS_ERR_VALUE(err)) { > > + err = mmc_select_hs_ddr(card, ext_csd); > > + if (err) > > + goto err; > > } > > - > > - ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? > > - EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; > > - err = mmc_select_powerclass(card, ext_csd_bits); > > - if (err) > > - pr_warning("%s: power class selection to bus width %d" > > - " failed\n", mmc_hostname(card->host), > > - 1 << bus_width); > > } > > > > /* > > - * Activate wide bus and DDR (if supported). > > + * Choose the power calss with selected bus interface > > */ > > - if (!mmc_card_hs200(card) && > > - (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && > > - (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { > > - static unsigned ext_csd_bits[][2] = { > > - { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, > > - { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, > > - { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, > > - }; > > - static unsigned bus_widths[] = { > > - MMC_BUS_WIDTH_8, > > - MMC_BUS_WIDTH_4, > > - MMC_BUS_WIDTH_1 > > - }; > > - unsigned idx, bus_width = 0; > > - > > - if (host->caps & MMC_CAP_8_BIT_DATA) > > - idx = 0; > > - else > > - idx = 1; > > - for (; idx < ARRAY_SIZE(bus_widths); idx++) { > > - bus_width = bus_widths[idx]; > > - if (bus_width == MMC_BUS_WIDTH_1) > > - ddr = 0; /* no DDR for 1-bit width */ > > - err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); > > - if (err) > > - pr_warning("%s: power class selection to " > > - "bus width %d failed\n", > > - mmc_hostname(card->host), > > - 1 << bus_width); > > - > > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > > - EXT_CSD_BUS_WIDTH, > > - ext_csd_bits[idx][0], > > - card->ext_csd.generic_cmd6_time); > > - if (!err) { > > - mmc_set_bus_width(card->host, bus_width); > > - > > - /* > > - * If controller can't handle bus width test, > > - * compare ext_csd previously read in 1 bit mode > > - * against ext_csd at new bus width > > - */ > > - if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) > > - err = mmc_compare_ext_csds(card, > > - bus_width); > > - else > > - err = mmc_bus_test(card, bus_width); > > - if (!err) > > - break; > > - } > > - } > > - > > - if (!err && ddr) { > > - err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); > > - if (err) > > - pr_warning("%s: power class selection to " > > - "bus width %d ddr %d failed\n", > > - mmc_hostname(card->host), > > - 1 << bus_width, ddr); > > - > > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, > > - EXT_CSD_BUS_WIDTH, > > - ext_csd_bits[idx][1], > > - card->ext_csd.generic_cmd6_time); > > - } > > - if (err) { > > - pr_warning("%s: switch to bus width %d ddr %d " > > - "failed\n", mmc_hostname(card->host), > > - 1 << bus_width, ddr); > > - goto free_card; > > - } else if (ddr) { > > - /* > > - * eMMC cards can support 3.3V to 1.2V i/o (vccq) > > - * signaling. > > - * > > - * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. > > - * > > - * 1.8V vccq at 3.3V core voltage (vcc) is not required > > - * in the JEDEC spec for DDR. > > - * > > - * Do not force change in vccq since we are obviously > > - * working and no change to vccq is needed. > > - * > > - * WARNING: eMMC rules are NOT the same as SD DDR > > - */ > > - if (ddr == MMC_1_2V_DDR_MODE) { > > - err = __mmc_set_signal_voltage(host, > > - MMC_SIGNAL_VOLTAGE_120); > > - if (err) > > - goto err; > > - } > > - mmc_card_set_ddr_mode(card); > > - mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); > > - mmc_set_bus_width(card->host, bus_width); > > - } > > - } > > + mmc_select_powerclass(card, ext_csd); > > > > /* > > * Enable HPI feature (if supported) > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h > > index 176fdf8..c119735 100644 > > --- a/include/linux/mmc/card.h > > +++ b/include/linux/mmc/card.h > > @@ -63,6 +63,7 @@ struct mmc_ext_csd { > > unsigned int power_off_longtime; /* Units: ms */ > > u8 power_off_notification; /* state */ > > unsigned int hs_max_dtr; > > + unsigned int hs200_max_dtr; > > #define MMC_HIGH_26_MAX_DTR 26000000 > > #define MMC_HIGH_52_MAX_DTR 52000000 > > #define MMC_HIGH_DDR_MAX_DTR 52000000 > > @@ -299,7 +300,10 @@ struct mmc_card { > > const char **info; /* info strings */ > > struct sdio_func_tuple *tuples; /* unknown common tuples */ > > > > - unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ > > + union { > > + unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ > > + unsigned int mmc_avail_type; /* supported device type by both host and card */ > > + }; > > > > struct dentry *debugfs_root; > > struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h > > index 99f5709..69d58b1 100644 > > --- a/include/linux/mmc/host.h > > +++ b/include/linux/mmc/host.h > > @@ -60,12 +60,6 @@ struct mmc_ios { > > #define MMC_TIMING_UHS_DDR50 7 > > #define MMC_TIMING_MMC_HS200 8 > > > > -#define MMC_SDR_MODE 0 > > -#define MMC_1_2V_DDR_MODE 1 > > -#define MMC_1_8V_DDR_MODE 2 > > -#define MMC_1_2V_SDR_MODE 3 > > -#define MMC_1_8V_SDR_MODE 4 > > - > > unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ > > > > #define MMC_SIGNAL_VOLTAGE_330 0 > > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h > > index 50bcde3..87df508 100644 > > --- a/include/linux/mmc/mmc.h > > +++ b/include/linux/mmc/mmc.h > > @@ -373,6 +373,10 @@ struct _mmc_csd { > > #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ > > #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ > > > > +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ > > +#define EXT_CSD_TIMING_HS 1 /* High speed */ > > +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ > > + > > #define EXT_CSD_SEC_ER_EN BIT(0) > > #define EXT_CSD_SEC_BD_BLK_EN BIT(2) > > #define EXT_CSD_SEC_GB_CL_EN BIT(4) > > -- > > 1.7.0.4 > > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-mmc" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 6 November 2013 10:09, Seungwon Jeon <tgih.jun@samsung.com> wrote: > Hi Ulf, > > On Tue, November 05, 2013, Ulf Hansson wrote: >> Hi Seungwon, >> >> On 5 November 2013 14:27, Seungwon Jeon <tgih.jun@samsung.com> wrote: >> > Current implementation for bus speed mode selection is too >> > complicated. This patch is to simplify the codes and remove >> > some duplicate parts. >> > >> > The following changes are including: >> > * Adds functions for each mode selection(HS, HS-DDR, HS200 and etc) >> > * Rearranged the mode selection sequence with supported device type >> > * Power class is switched once only after bus modes(speed/width) >> > are selected finally >> > * Adds maximum speed for HS200 mode(hs200_max_dtr) >> > * Adds available device type to be supported by both host and device >> > * Adds field definition for HS_TIMING of EXT_CSD >> >> Very nice work you are doing here, this is certainly needed. >> >> Although, from a reviewing point of view it would be nice if you could >> split up and do re-factoring in more minor pieces. Could that be done? > > Ok. I'll consider that. > But I guess it would not be done nicely because a whole flow is organic. > If you have any opinion, please let me know. Yes, please split it up. Certainly it can be done nicely as well. In your commit message you kind of already have a step by step procedure how the split up can be done, just adopt to it. :-) Kind regards Ulf Hansson > > Thanks, > Seungwon Jeon > >> >> Kind regards >> Ulf Hansson >> >> > >> > Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> >> > --- >> > drivers/mmc/core/mmc.c | 574 +++++++++++++++++++++++++++------------------- >> > include/linux/mmc/card.h | 6 +- >> > include/linux/mmc/host.h | 6 - >> > include/linux/mmc/mmc.h | 4 + >> > 4 files changed, 345 insertions(+), 245 deletions(-) >> > >> > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c >> > index f631f5a..f4f8991 100644 >> > --- a/drivers/mmc/core/mmc.c >> > +++ b/drivers/mmc/core/mmc.c >> > @@ -242,29 +242,48 @@ static void mmc_select_card_type(struct mmc_card *card) >> > struct mmc_host *host = card->host; >> > u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; >> > u32 caps = host->caps, caps2 = host->caps2; >> > - unsigned int hs_max_dtr = 0; >> > + unsigned int avail_type = 0; >> > + unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; >> > >> > - if (card_type & EXT_CSD_CARD_TYPE_26) >> > + if (card_type & EXT_CSD_CARD_TYPE_26) { >> > hs_max_dtr = MMC_HIGH_26_MAX_DTR; >> > + avail_type |= EXT_CSD_CARD_TYPE_26; >> > + } >> > >> > if (caps & MMC_CAP_MMC_HIGHSPEED && >> > - card_type & EXT_CSD_CARD_TYPE_52) >> > + card_type & EXT_CSD_CARD_TYPE_52) { >> > hs_max_dtr = MMC_HIGH_52_MAX_DTR; >> > + avail_type |= EXT_CSD_CARD_TYPE_52; >> > + } >> > + >> > + if (caps & MMC_CAP_1_8V_DDR && >> > + card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { >> > + hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; >> > + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; >> > + } >> > >> > - if ((caps & MMC_CAP_1_8V_DDR && >> > - card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) || >> > - (caps & MMC_CAP_1_2V_DDR && >> > - card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)) >> > + if (caps & MMC_CAP_1_2V_DDR && >> > + card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { >> > hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; >> > + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; >> > + } >> > + >> > + if (caps2 & MMC_CAP2_HS200_1_8V_SDR && >> > + card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) { >> > + hs200_max_dtr = MMC_HS200_MAX_DTR; >> > + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_8V; >> > + } >> > >> > - if ((caps2 & MMC_CAP2_HS200_1_8V_SDR && >> > - card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) || >> > - (caps2 & MMC_CAP2_HS200_1_2V_SDR && >> > - card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)) >> > - hs_max_dtr = MMC_HS200_MAX_DTR; >> > + if (caps2 & MMC_CAP2_HS200_1_2V_SDR && >> > + card_type & EXT_CSD_CARD_TYPE_SDR_1_2V) { >> > + hs200_max_dtr = MMC_HS200_MAX_DTR; >> > + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_2V; >> > + } >> > >> > card->ext_csd.hs_max_dtr = hs_max_dtr; >> > + card->ext_csd.hs200_max_dtr = hs200_max_dtr; >> > card->ext_csd.card_type = card_type; >> > + card->mmc_avail_type = avail_type; >> > } >> > >> > /* >> > @@ -714,8 +733,8 @@ static struct device_type mmc_type = { >> > * extended CSD register, select it by executing the >> > * mmc_switch command. >> > */ >> > -static int mmc_select_powerclass(struct mmc_card *card, >> > - unsigned int bus_width) >> > +static int __mmc_select_powerclass(struct mmc_card *card, >> > + unsigned int bus_width, u8 *ext_csd) >> > { >> > int err = 0; >> > unsigned int pwrclass_val = 0; >> > @@ -787,40 +806,99 @@ static int mmc_select_powerclass(struct mmc_card *card, >> > return err; >> > } >> > >> > +static inline unsigned int mmc_snoop_ddr(struct mmc_card *card) >> > +{ >> > + return card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; >> > +} >> > + >> > +static int mmc_select_powerclass(struct mmc_card *card, u8 *ext_csd) >> > +{ >> > + int err, ddr; >> > + u32 bus_width, ext_csd_bits; >> > + struct mmc_host *host; >> > + >> > + BUG_ON(!card); >> > + >> > + host = card->host; >> > + >> > + if (!ext_csd) >> > + return 0; >> > + >> > + /* Power class selection is supported for versions >= 4.0 */ >> > + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) >> > + return 0; >> > + >> > + bus_width = host->ios.bus_width; >> > + /* Power class values are defined only for 4/8 bit bus */ >> > + if (bus_width == MMC_BUS_WIDTH_1) >> > + return 0; >> > + >> > + ddr = mmc_snoop_ddr(card); >> > + if (ddr) >> > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? >> > + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; >> > + else >> > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? >> > + EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; >> > + >> > + err = __mmc_select_powerclass(card, ext_csd_bits, ext_csd); >> > + if (err) >> > + pr_warn("%s: power class selection to bus width %d ddr %d failed\n", >> > + mmc_hostname(host), 1 << bus_width, ddr); >> > + >> > + return err; >> > +} >> > + >> > /* >> > - * Selects the desired buswidth and switch to the HS200 mode >> > - * if bus width set without error >> > + * Set the bus speed for the selected speed mode. >> > */ >> > -static int mmc_select_hs200(struct mmc_card *card) >> > +static void mmc_set_bus_speed(struct mmc_card *card) >> > +{ >> > + unsigned int max_dtr = (unsigned int)-1; >> > + >> > + BUG_ON(!card); >> > + >> > + if (mmc_card_hs200(card)) { >> > + if (max_dtr > card->ext_csd.hs200_max_dtr) >> > + max_dtr = card->ext_csd.hs200_max_dtr; >> > + } else if (mmc_card_highspeed(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) { >> > + max_dtr = card->csd.max_dtr; >> > + } >> > + >> > + mmc_set_clock(card->host, max_dtr); >> > +} >> > + >> > +/* >> > + * Select the bus width amoung 4-bit and 8-bit(SDR). >> > + * If the bus width is changed successfully, return the slected width value. >> > + * Zero is returned instead of error value if the wide width is not supported. >> > + */ >> > +static int mmc_select_bus_width(struct mmc_card *card) >> > { >> > - int idx, err = -EINVAL; >> > - struct mmc_host *host; >> > static unsigned ext_csd_bits[] = { >> > - EXT_CSD_BUS_WIDTH_4, >> > EXT_CSD_BUS_WIDTH_8, >> > + EXT_CSD_BUS_WIDTH_4, >> > }; >> > static unsigned bus_widths[] = { >> > - MMC_BUS_WIDTH_4, >> > MMC_BUS_WIDTH_8, >> > + MMC_BUS_WIDTH_4, >> > }; >> > + struct mmc_host *host; >> > + unsigned idx, bus_width = 0; >> > + int err = 0; >> > >> > BUG_ON(!card); >> > >> > host = card->host; >> > >> > - if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && >> > - host->caps2 & MMC_CAP2_HS200_1_2V_SDR) >> > - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); >> > - >> > - if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && >> > - host->caps2 & MMC_CAP2_HS200_1_8V_SDR) >> > - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); >> > - >> > - /* If fails try again during next card power cycle */ >> > - if (err) >> > - goto err; >> > + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && >> > + !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) >> > + return 0; >> > >> > - idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; >> > + idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1; >> > >> > /* >> > * Unlike SD, MMC cards dont have a configuration register to notify >> > @@ -828,8 +906,7 @@ static int mmc_select_hs200(struct mmc_card *card) >> > * the supported bus width or compare the ext csd values of current >> > * bus width and ext csd values of 1 bit mode read earlier. >> > */ >> > - for (; idx >= 0; idx--) { >> > - >> > + for (; idx < ARRAY_SIZE(bus_widths); idx++) { >> > /* >> > * Host is capable of 8bit transfer, then switch >> > * the device to work in 8bit transfer mode. If the >> > @@ -844,25 +921,226 @@ static int mmc_select_hs200(struct mmc_card *card) >> > if (err) >> > continue; >> > >> > - mmc_set_bus_width(card->host, bus_widths[idx]); >> > + bus_width = bus_widths[idx]; >> > + mmc_set_bus_width(host, bus_width); >> > >> > + /* >> > + * If controller can't handle bus width test, >> > + * compare ext_csd previously read in 1 bit mode >> > + * against ext_csd at new bus width >> > + */ >> > if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) >> > - err = mmc_compare_ext_csds(card, bus_widths[idx]); >> > + err = mmc_compare_ext_csds(card, bus_width); >> > else >> > - err = mmc_bus_test(card, bus_widths[idx]); >> > - if (!err) >> > + err = mmc_bus_test(card, bus_width); >> > + >> > + if (!err) { >> > + err = bus_width; >> > break; >> > + } else { >> > + pr_warn("%s: switch to bus width %d failed\n", >> > + mmc_hostname(card->host), ext_csd_bits[idx]); >> > + } >> > } >> > >> > - /* switch to HS200 mode if bus width set successfully */ >> > - if (!err) >> > + return err; >> > +} >> > + >> > +/* >> > + * Switch to the high-speed mode >> > + */ >> > +static int mmc_select_hs(struct mmc_card *card) >> > +{ >> > + int err; >> > + >> > + BUG_ON(!card); >> > + >> > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, >> > + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, >> > + card->ext_csd.generic_cmd6_time); >> > + if (!err) { >> > + mmc_card_set_highspeed(card); >> > + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); >> > + } >> > + >> > + return err; >> > +} >> > + >> > +/* >> > + * Activate wide bus and DDR if supported. >> > + */ >> > +static int mmc_select_hs_ddr(struct mmc_card *card, u8 *ext_csd) >> > +{ >> > + struct mmc_host *host; >> > + u32 bus_width, ext_csd_bits; >> > + int err = 0, ddr; >> > + >> > + BUG_ON(!card); >> > + >> > + ddr = mmc_snoop_ddr(card); >> > + if (!(ddr & EXT_CSD_CARD_TYPE_DDR_52)) >> > + return 0; >> > + >> > + host = card->host; >> > + bus_width = host->ios.bus_width; >> > + if (bus_width == MMC_BUS_WIDTH_1) >> > + return 0; >> > + >> > + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? >> > + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; >> > + >> > + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, >> > + EXT_CSD_BUS_WIDTH, >> > + ext_csd_bits, >> > + card->ext_csd.generic_cmd6_time); >> > + if (err) { >> > + pr_warn("%s: switch to bus width %d ddr failed\n", >> > + mmc_hostname(host), 1 << bus_width); >> > + return err; >> > + } >> > + >> > + /* >> > + * eMMC cards can support 3.3V to 1.2V i/o (vccq) >> > + * signaling. >> > + * >> > + * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. >> > + * >> > + * 1.8V vccq at 3.3V core voltage (vcc) is not required >> > + * in the JEDEC spec for DDR. >> > + * >> > + * Do not force change in vccq since we are obviously >> > + * working and no change to vccq is needed. >> > + * >> > + * WARNING: eMMC rules are NOT the same as SD DDR >> > + */ >> > + if (ddr & EXT_CSD_CARD_TYPE_DDR_1_2V) { >> > + err = __mmc_set_signal_voltage(host, >> > + MMC_SIGNAL_VOLTAGE_120); >> > + if (err) >> > + return err; >> > + } >> > + >> > + mmc_card_set_ddr_mode(card); >> > + mmc_set_timing(host, MMC_TIMING_UHS_DDR50); >> > + >> > + return err; >> > +} >> > + >> > +/* >> > + * For device supporting HS200 mode, the following sequence >> > + * should be done before executing the tuning process. >> > + * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported) >> > + * 2. switch to HS200 mode >> > + * 3. set the clock to > 52Mhz and <=200MHz >> > + */ >> > +static int mmc_select_hs200(struct mmc_card *card) >> > +{ >> > + int err = -EINVAL; >> > + struct mmc_host *host; >> > + >> > + BUG_ON(!card); >> > + >> > + host = card->host; >> > + >> > + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_2V) >> > + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); >> > + >> > + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_8V) >> > + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); >> > + >> > + /* If fails try again during next card power cycle */ >> > + if (err) >> > + goto err; >> > + >> > + /* >> > + * Set the bus width(4 or 8) with host's support and >> > + * switch to HS200 mode if bus width is set successfully. >> > + */ >> > + err = mmc_select_bus_width(card); >> > + if (!IS_ERR_VALUE(err)) { >> > err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, >> > - EXT_CSD_HS_TIMING, 2, 0); >> > + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, >> > + card->ext_csd.generic_cmd6_time); >> > + if (!err) { >> > + mmc_card_set_hs200(card); >> > + mmc_set_timing(host, MMC_TIMING_MMC_HS200); >> > + } >> > + } >> > err: >> > return err; >> > } >> > >> > /* >> > + * Activate High Speed or HS200 mode if supported. >> > + */ >> > +static int mmc_select_timing(struct mmc_card *card) >> > +{ >> > + struct mmc_host *host; >> > + int err = 0; >> > + >> > + BUG_ON(!card); >> > + >> > + host = card->host; >> > + >> > + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && >> > + card->ext_csd.hs_max_dtr == 0)) >> > + goto bus_speed; >> > + >> > + if (card->ext_csd.hs200_max_dtr > 52000000 && >> > + host->caps2 & MMC_CAP2_HS200) >> > + err = mmc_select_hs200(card); >> > + else if (host->caps & MMC_CAP_MMC_HIGHSPEED) >> > + err = mmc_select_hs(card); >> > + >> > + if (err && err != -EBADMSG) >> > + return err; >> > + >> > + if (err) { >> > + pr_warn("%s: switch to %s failed\n", >> > + mmc_card_highspeed(card) ? "high-speed" : >> > + (mmc_card_hs200(card) ? "hs200" : ""), >> > + mmc_hostname(card->host)); >> > + err = 0; >> > + } >> > + >> > +bus_speed: >> > + /* >> > + * Set the bus speed to the selected bus timing. >> > + * If timing is not selected, backward compatible is the default. >> > + */ >> > + mmc_set_bus_speed(card); >> > + return err; >> > +} >> > + >> > +/* >> > + * Execute tuning sequence to seek the proper bus operating >> > + * conditions for HS200, which sends CMD21 to the device. >> > + */ >> > +static int mmc_hs200_tuning(struct mmc_card *card) >> > +{ >> > + int err = 0; >> > + struct mmc_host *host; >> > + >> > + BUG_ON(!card); >> > + >> > + host = card->host; >> > + >> > + if (host->caps2 & MMC_CAP2_HS200 && >> > + card->host->ops->execute_tuning) { >> > + mmc_host_clk_hold(card->host); >> > + err = card->host->ops->execute_tuning(card->host, >> > + MMC_SEND_TUNING_BLOCK_HS200); >> > + mmc_host_clk_release(card->host); >> > + >> > + if (err) >> > + pr_warn("%s: tuning execution failed\n", >> > + mmc_hostname(card->host)); >> > + } >> > + >> > + return err; >> > +} >> > + >> > +/* >> > * Handle the detection and initialisation of a card. >> > * >> > * In the case of a resume, "oldcard" will contain the card >> > @@ -872,9 +1150,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, >> > struct mmc_card *oldcard) >> > { >> > struct mmc_card *card; >> > - int err, ddr = 0; >> > + int err; >> > u32 cid[4]; >> > - unsigned int max_dtr; >> > u32 rocr; >> > u8 *ext_csd = NULL; >> > >> > @@ -1066,209 +1343,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, >> > } >> > >> > /* >> > - * Activate high speed (if supported) >> > - */ >> > - if (card->ext_csd.hs_max_dtr != 0) { >> > - err = 0; >> > - if (card->ext_csd.hs_max_dtr > 52000000 && >> > - host->caps2 & MMC_CAP2_HS200) >> > - err = mmc_select_hs200(card); >> > - else if (host->caps & MMC_CAP_MMC_HIGHSPEED) >> > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, >> > - EXT_CSD_HS_TIMING, 1, >> > - card->ext_csd.generic_cmd6_time); >> > - >> > - if (err && err != -EBADMSG) >> > - goto free_card; >> > - >> > - if (err) { >> > - pr_warning("%s: switch to highspeed failed\n", >> > - mmc_hostname(card->host)); >> > - err = 0; >> > - } else { >> > - if (card->ext_csd.hs_max_dtr > 52000000 && >> > - host->caps2 & MMC_CAP2_HS200) { >> > - mmc_card_set_hs200(card); >> > - mmc_set_timing(card->host, >> > - MMC_TIMING_MMC_HS200); >> > - } else { >> > - mmc_card_set_highspeed(card); >> > - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); >> > - } >> > - } >> > - } >> > - >> > - /* >> > - * Compute bus speed. >> > - */ >> > - max_dtr = (unsigned int)-1; >> > - >> > - if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { >> > - if (max_dtr > card->ext_csd.hs_max_dtr) >> > - max_dtr = card->ext_csd.hs_max_dtr; >> > - if (mmc_card_highspeed(card) && (max_dtr > 52000000)) >> > - max_dtr = 52000000; >> > - } else if (max_dtr > card->csd.max_dtr) { >> > - max_dtr = card->csd.max_dtr; >> > - } >> > - >> > - mmc_set_clock(host, max_dtr); >> > - >> > - /* >> > - * Indicate DDR mode (if supported). >> > + * Select timing interface >> > */ >> > - if (mmc_card_highspeed(card)) { >> > - if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) >> > - && ((host->caps & (MMC_CAP_1_8V_DDR | >> > - MMC_CAP_UHS_DDR50)) >> > - == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) >> > - ddr = MMC_1_8V_DDR_MODE; >> > - else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) >> > - && ((host->caps & (MMC_CAP_1_2V_DDR | >> > - MMC_CAP_UHS_DDR50)) >> > - == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) >> > - ddr = MMC_1_2V_DDR_MODE; >> > - } >> > + err = mmc_select_timing(card); >> > + if (err) >> > + goto free_card; >> > >> > - /* >> > - * Indicate HS200 SDR mode (if supported). >> > - */ >> > if (mmc_card_hs200(card)) { >> > - u32 ext_csd_bits; >> > - u32 bus_width = card->host->ios.bus_width; >> > - >> > - /* >> > - * For devices supporting HS200 mode, the bus width has >> > - * to be set before executing the tuning function. If >> > - * set before tuning, then device will respond with CRC >> > - * errors for responses on CMD line. So for HS200 the >> > - * sequence will be >> > - * 1. set bus width 4bit / 8 bit (1 bit not supported) >> > - * 2. switch to HS200 mode >> > - * 3. set the clock to > 52Mhz <=200MHz and >> > - * 4. execute tuning for HS200 >> > - */ >> > - if ((host->caps2 & MMC_CAP2_HS200) && >> > - card->host->ops->execute_tuning) { >> > - mmc_host_clk_hold(card->host); >> > - err = card->host->ops->execute_tuning(card->host, >> > - MMC_SEND_TUNING_BLOCK_HS200); >> > - mmc_host_clk_release(card->host); >> > - } >> > - if (err) { >> > - pr_warning("%s: tuning execution failed\n", >> > - mmc_hostname(card->host)); >> > + err = mmc_hs200_tuning(card); >> > + if (err) >> > goto err; >> > + } else if (mmc_card_highspeed(card)) { >> > + /* Select the desired bus width optionally */ >> > + err = mmc_select_bus_width(card); >> > + if (!IS_ERR_VALUE(err)) { >> > + err = mmc_select_hs_ddr(card, ext_csd); >> > + if (err) >> > + goto err; >> > } >> > - >> > - ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? >> > - EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; >> > - err = mmc_select_powerclass(card, ext_csd_bits); >> > - if (err) >> > - pr_warning("%s: power class selection to bus width %d" >> > - " failed\n", mmc_hostname(card->host), >> > - 1 << bus_width); >> > } >> > >> > /* >> > - * Activate wide bus and DDR (if supported). >> > + * Choose the power calss with selected bus interface >> > */ >> > - if (!mmc_card_hs200(card) && >> > - (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && >> > - (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { >> > - static unsigned ext_csd_bits[][2] = { >> > - { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, >> > - { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, >> > - { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, >> > - }; >> > - static unsigned bus_widths[] = { >> > - MMC_BUS_WIDTH_8, >> > - MMC_BUS_WIDTH_4, >> > - MMC_BUS_WIDTH_1 >> > - }; >> > - unsigned idx, bus_width = 0; >> > - >> > - if (host->caps & MMC_CAP_8_BIT_DATA) >> > - idx = 0; >> > - else >> > - idx = 1; >> > - for (; idx < ARRAY_SIZE(bus_widths); idx++) { >> > - bus_width = bus_widths[idx]; >> > - if (bus_width == MMC_BUS_WIDTH_1) >> > - ddr = 0; /* no DDR for 1-bit width */ >> > - err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); >> > - if (err) >> > - pr_warning("%s: power class selection to " >> > - "bus width %d failed\n", >> > - mmc_hostname(card->host), >> > - 1 << bus_width); >> > - >> > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, >> > - EXT_CSD_BUS_WIDTH, >> > - ext_csd_bits[idx][0], >> > - card->ext_csd.generic_cmd6_time); >> > - if (!err) { >> > - mmc_set_bus_width(card->host, bus_width); >> > - >> > - /* >> > - * If controller can't handle bus width test, >> > - * compare ext_csd previously read in 1 bit mode >> > - * against ext_csd at new bus width >> > - */ >> > - if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) >> > - err = mmc_compare_ext_csds(card, >> > - bus_width); >> > - else >> > - err = mmc_bus_test(card, bus_width); >> > - if (!err) >> > - break; >> > - } >> > - } >> > - >> > - if (!err && ddr) { >> > - err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); >> > - if (err) >> > - pr_warning("%s: power class selection to " >> > - "bus width %d ddr %d failed\n", >> > - mmc_hostname(card->host), >> > - 1 << bus_width, ddr); >> > - >> > - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, >> > - EXT_CSD_BUS_WIDTH, >> > - ext_csd_bits[idx][1], >> > - card->ext_csd.generic_cmd6_time); >> > - } >> > - if (err) { >> > - pr_warning("%s: switch to bus width %d ddr %d " >> > - "failed\n", mmc_hostname(card->host), >> > - 1 << bus_width, ddr); >> > - goto free_card; >> > - } else if (ddr) { >> > - /* >> > - * eMMC cards can support 3.3V to 1.2V i/o (vccq) >> > - * signaling. >> > - * >> > - * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. >> > - * >> > - * 1.8V vccq at 3.3V core voltage (vcc) is not required >> > - * in the JEDEC spec for DDR. >> > - * >> > - * Do not force change in vccq since we are obviously >> > - * working and no change to vccq is needed. >> > - * >> > - * WARNING: eMMC rules are NOT the same as SD DDR >> > - */ >> > - if (ddr == MMC_1_2V_DDR_MODE) { >> > - err = __mmc_set_signal_voltage(host, >> > - MMC_SIGNAL_VOLTAGE_120); >> > - if (err) >> > - goto err; >> > - } >> > - mmc_card_set_ddr_mode(card); >> > - mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); >> > - mmc_set_bus_width(card->host, bus_width); >> > - } >> > - } >> > + mmc_select_powerclass(card, ext_csd); >> > >> > /* >> > * Enable HPI feature (if supported) >> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h >> > index 176fdf8..c119735 100644 >> > --- a/include/linux/mmc/card.h >> > +++ b/include/linux/mmc/card.h >> > @@ -63,6 +63,7 @@ struct mmc_ext_csd { >> > unsigned int power_off_longtime; /* Units: ms */ >> > u8 power_off_notification; /* state */ >> > unsigned int hs_max_dtr; >> > + unsigned int hs200_max_dtr; >> > #define MMC_HIGH_26_MAX_DTR 26000000 >> > #define MMC_HIGH_52_MAX_DTR 52000000 >> > #define MMC_HIGH_DDR_MAX_DTR 52000000 >> > @@ -299,7 +300,10 @@ struct mmc_card { >> > const char **info; /* info strings */ >> > struct sdio_func_tuple *tuples; /* unknown common tuples */ >> > >> > - unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ >> > + union { >> > + unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ >> > + unsigned int mmc_avail_type; /* supported device type by both host and card */ >> > + }; >> > >> > struct dentry *debugfs_root; >> > struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ >> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h >> > index 99f5709..69d58b1 100644 >> > --- a/include/linux/mmc/host.h >> > +++ b/include/linux/mmc/host.h >> > @@ -60,12 +60,6 @@ struct mmc_ios { >> > #define MMC_TIMING_UHS_DDR50 7 >> > #define MMC_TIMING_MMC_HS200 8 >> > >> > -#define MMC_SDR_MODE 0 >> > -#define MMC_1_2V_DDR_MODE 1 >> > -#define MMC_1_8V_DDR_MODE 2 >> > -#define MMC_1_2V_SDR_MODE 3 >> > -#define MMC_1_8V_SDR_MODE 4 >> > - >> > unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ >> > >> > #define MMC_SIGNAL_VOLTAGE_330 0 >> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h >> > index 50bcde3..87df508 100644 >> > --- a/include/linux/mmc/mmc.h >> > +++ b/include/linux/mmc/mmc.h >> > @@ -373,6 +373,10 @@ struct _mmc_csd { >> > #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ >> > #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ >> > >> > +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ >> > +#define EXT_CSD_TIMING_HS 1 /* High speed */ >> > +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ >> > + >> > #define EXT_CSD_SEC_ER_EN BIT(0) >> > #define EXT_CSD_SEC_BD_BLK_EN BIT(2) >> > #define EXT_CSD_SEC_GB_CL_EN BIT(4) >> > -- >> > 1.7.0.4 >> > >> > >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > -- > To unsubscribe from this list: send the line "unsubscribe linux-mmc" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f631f5a..f4f8991 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -242,29 +242,48 @@ static void mmc_select_card_type(struct mmc_card *card) struct mmc_host *host = card->host; u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; u32 caps = host->caps, caps2 = host->caps2; - unsigned int hs_max_dtr = 0; + unsigned int avail_type = 0; + unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; - if (card_type & EXT_CSD_CARD_TYPE_26) + if (card_type & EXT_CSD_CARD_TYPE_26) { hs_max_dtr = MMC_HIGH_26_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_26; + } if (caps & MMC_CAP_MMC_HIGHSPEED && - card_type & EXT_CSD_CARD_TYPE_52) + card_type & EXT_CSD_CARD_TYPE_52) { hs_max_dtr = MMC_HIGH_52_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_52; + } + + if (caps & MMC_CAP_1_8V_DDR && + card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { + hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; + } - if ((caps & MMC_CAP_1_8V_DDR && - card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) || - (caps & MMC_CAP_1_2V_DDR && - card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)) + if (caps & MMC_CAP_1_2V_DDR && + card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; + } + + if (caps2 & MMC_CAP2_HS200_1_8V_SDR && + card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) { + hs200_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_8V; + } - if ((caps2 & MMC_CAP2_HS200_1_8V_SDR && - card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) || - (caps2 & MMC_CAP2_HS200_1_2V_SDR && - card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)) - hs_max_dtr = MMC_HS200_MAX_DTR; + if (caps2 & MMC_CAP2_HS200_1_2V_SDR && + card_type & EXT_CSD_CARD_TYPE_SDR_1_2V) { + hs200_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_SDR_1_2V; + } card->ext_csd.hs_max_dtr = hs_max_dtr; + card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->ext_csd.card_type = card_type; + card->mmc_avail_type = avail_type; } /* @@ -714,8 +733,8 @@ static struct device_type mmc_type = { * extended CSD register, select it by executing the * mmc_switch command. */ -static int mmc_select_powerclass(struct mmc_card *card, - unsigned int bus_width) +static int __mmc_select_powerclass(struct mmc_card *card, + unsigned int bus_width, u8 *ext_csd) { int err = 0; unsigned int pwrclass_val = 0; @@ -787,40 +806,99 @@ static int mmc_select_powerclass(struct mmc_card *card, return err; } +static inline unsigned int mmc_snoop_ddr(struct mmc_card *card) +{ + return card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; +} + +static int mmc_select_powerclass(struct mmc_card *card, u8 *ext_csd) +{ + int err, ddr; + u32 bus_width, ext_csd_bits; + struct mmc_host *host; + + BUG_ON(!card); + + host = card->host; + + if (!ext_csd) + return 0; + + /* Power class selection is supported for versions >= 4.0 */ + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) + return 0; + + bus_width = host->ios.bus_width; + /* Power class values are defined only for 4/8 bit bus */ + if (bus_width == MMC_BUS_WIDTH_1) + return 0; + + ddr = mmc_snoop_ddr(card); + if (ddr) + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; + else + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? + EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; + + err = __mmc_select_powerclass(card, ext_csd_bits, ext_csd); + if (err) + pr_warn("%s: power class selection to bus width %d ddr %d failed\n", + mmc_hostname(host), 1 << bus_width, ddr); + + return err; +} + /* - * Selects the desired buswidth and switch to the HS200 mode - * if bus width set without error + * Set the bus speed for the selected speed mode. */ -static int mmc_select_hs200(struct mmc_card *card) +static void mmc_set_bus_speed(struct mmc_card *card) +{ + unsigned int max_dtr = (unsigned int)-1; + + BUG_ON(!card); + + if (mmc_card_hs200(card)) { + if (max_dtr > card->ext_csd.hs200_max_dtr) + max_dtr = card->ext_csd.hs200_max_dtr; + } else if (mmc_card_highspeed(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) { + max_dtr = card->csd.max_dtr; + } + + mmc_set_clock(card->host, max_dtr); +} + +/* + * Select the bus width amoung 4-bit and 8-bit(SDR). + * If the bus width is changed successfully, return the slected width value. + * Zero is returned instead of error value if the wide width is not supported. + */ +static int mmc_select_bus_width(struct mmc_card *card) { - int idx, err = -EINVAL; - struct mmc_host *host; static unsigned ext_csd_bits[] = { - EXT_CSD_BUS_WIDTH_4, EXT_CSD_BUS_WIDTH_8, + EXT_CSD_BUS_WIDTH_4, }; static unsigned bus_widths[] = { - MMC_BUS_WIDTH_4, MMC_BUS_WIDTH_8, + MMC_BUS_WIDTH_4, }; + struct mmc_host *host; + unsigned idx, bus_width = 0; + int err = 0; BUG_ON(!card); host = card->host; - if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && - host->caps2 & MMC_CAP2_HS200_1_2V_SDR) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); - - if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && - host->caps2 & MMC_CAP2_HS200_1_8V_SDR) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); - - /* If fails try again during next card power cycle */ - if (err) - goto err; + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && + !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) + return 0; - idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; + idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1; /* * Unlike SD, MMC cards dont have a configuration register to notify @@ -828,8 +906,7 @@ static int mmc_select_hs200(struct mmc_card *card) * the supported bus width or compare the ext csd values of current * bus width and ext csd values of 1 bit mode read earlier. */ - for (; idx >= 0; idx--) { - + for (; idx < ARRAY_SIZE(bus_widths); idx++) { /* * Host is capable of 8bit transfer, then switch * the device to work in 8bit transfer mode. If the @@ -844,25 +921,226 @@ static int mmc_select_hs200(struct mmc_card *card) if (err) continue; - mmc_set_bus_width(card->host, bus_widths[idx]); + bus_width = bus_widths[idx]; + mmc_set_bus_width(host, bus_width); + /* + * If controller can't handle bus width test, + * compare ext_csd previously read in 1 bit mode + * against ext_csd at new bus width + */ if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) - err = mmc_compare_ext_csds(card, bus_widths[idx]); + err = mmc_compare_ext_csds(card, bus_width); else - err = mmc_bus_test(card, bus_widths[idx]); - if (!err) + err = mmc_bus_test(card, bus_width); + + if (!err) { + err = bus_width; break; + } else { + pr_warn("%s: switch to bus width %d failed\n", + mmc_hostname(card->host), ext_csd_bits[idx]); + } } - /* switch to HS200 mode if bus width set successfully */ - if (!err) + return err; +} + +/* + * Switch to the high-speed mode + */ +static int mmc_select_hs(struct mmc_card *card) +{ + int err; + + BUG_ON(!card); + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, + card->ext_csd.generic_cmd6_time); + if (!err) { + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + } + + return err; +} + +/* + * Activate wide bus and DDR if supported. + */ +static int mmc_select_hs_ddr(struct mmc_card *card, u8 *ext_csd) +{ + struct mmc_host *host; + u32 bus_width, ext_csd_bits; + int err = 0, ddr; + + BUG_ON(!card); + + ddr = mmc_snoop_ddr(card); + if (!(ddr & EXT_CSD_CARD_TYPE_DDR_52)) + return 0; + + host = card->host; + bus_width = host->ios.bus_width; + if (bus_width == MMC_BUS_WIDTH_1) + return 0; + + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ext_csd_bits, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_warn("%s: switch to bus width %d ddr failed\n", + mmc_hostname(host), 1 << bus_width); + return err; + } + + /* + * eMMC cards can support 3.3V to 1.2V i/o (vccq) + * signaling. + * + * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. + * + * 1.8V vccq at 3.3V core voltage (vcc) is not required + * in the JEDEC spec for DDR. + * + * Do not force change in vccq since we are obviously + * working and no change to vccq is needed. + * + * WARNING: eMMC rules are NOT the same as SD DDR + */ + if (ddr & EXT_CSD_CARD_TYPE_DDR_1_2V) { + err = __mmc_set_signal_voltage(host, + MMC_SIGNAL_VOLTAGE_120); + if (err) + return err; + } + + mmc_card_set_ddr_mode(card); + mmc_set_timing(host, MMC_TIMING_UHS_DDR50); + + return err; +} + +/* + * For device supporting HS200 mode, the following sequence + * should be done before executing the tuning process. + * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported) + * 2. switch to HS200 mode + * 3. set the clock to > 52Mhz and <=200MHz + */ +static int mmc_select_hs200(struct mmc_card *card) +{ + int err = -EINVAL; + struct mmc_host *host; + + BUG_ON(!card); + + host = card->host; + + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_2V) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_SDR_1_8V) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + + /* If fails try again during next card power cycle */ + if (err) + goto err; + + /* + * Set the bus width(4 or 8) with host's support and + * switch to HS200 mode if bus width is set successfully. + */ + err = mmc_select_bus_width(card); + if (!IS_ERR_VALUE(err)) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 2, 0); + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, + card->ext_csd.generic_cmd6_time); + if (!err) { + mmc_card_set_hs200(card); + mmc_set_timing(host, MMC_TIMING_MMC_HS200); + } + } err: return err; } /* + * Activate High Speed or HS200 mode if supported. + */ +static int mmc_select_timing(struct mmc_card *card) +{ + struct mmc_host *host; + int err = 0; + + BUG_ON(!card); + + host = card->host; + + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && + card->ext_csd.hs_max_dtr == 0)) + goto bus_speed; + + if (card->ext_csd.hs200_max_dtr > 52000000 && + host->caps2 & MMC_CAP2_HS200) + err = mmc_select_hs200(card); + else if (host->caps & MMC_CAP_MMC_HIGHSPEED) + err = mmc_select_hs(card); + + if (err && err != -EBADMSG) + return err; + + if (err) { + pr_warn("%s: switch to %s failed\n", + mmc_card_highspeed(card) ? "high-speed" : + (mmc_card_hs200(card) ? "hs200" : ""), + mmc_hostname(card->host)); + err = 0; + } + +bus_speed: + /* + * Set the bus speed to the selected bus timing. + * If timing is not selected, backward compatible is the default. + */ + mmc_set_bus_speed(card); + return err; +} + +/* + * Execute tuning sequence to seek the proper bus operating + * conditions for HS200, which sends CMD21 to the device. + */ +static int mmc_hs200_tuning(struct mmc_card *card) +{ + int err = 0; + struct mmc_host *host; + + BUG_ON(!card); + + host = card->host; + + if (host->caps2 & MMC_CAP2_HS200 && + card->host->ops->execute_tuning) { + mmc_host_clk_hold(card->host); + err = card->host->ops->execute_tuning(card->host, + MMC_SEND_TUNING_BLOCK_HS200); + mmc_host_clk_release(card->host); + + if (err) + pr_warn("%s: tuning execution failed\n", + mmc_hostname(card->host)); + } + + return err; +} + +/* * Handle the detection and initialisation of a card. * * In the case of a resume, "oldcard" will contain the card @@ -872,9 +1150,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; - int err, ddr = 0; + int err; u32 cid[4]; - unsigned int max_dtr; u32 rocr; u8 *ext_csd = NULL; @@ -1066,209 +1343,30 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* - * Activate high speed (if supported) - */ - if (card->ext_csd.hs_max_dtr != 0) { - err = 0; - if (card->ext_csd.hs_max_dtr > 52000000 && - host->caps2 & MMC_CAP2_HS200) - err = mmc_select_hs200(card); - else if (host->caps & MMC_CAP_MMC_HIGHSPEED) - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1, - card->ext_csd.generic_cmd6_time); - - if (err && err != -EBADMSG) - goto free_card; - - if (err) { - pr_warning("%s: switch to highspeed failed\n", - mmc_hostname(card->host)); - err = 0; - } else { - if (card->ext_csd.hs_max_dtr > 52000000 && - host->caps2 & MMC_CAP2_HS200) { - mmc_card_set_hs200(card); - mmc_set_timing(card->host, - MMC_TIMING_MMC_HS200); - } else { - mmc_card_set_highspeed(card); - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - } - } - } - - /* - * Compute bus speed. - */ - max_dtr = (unsigned int)-1; - - if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { - if (max_dtr > card->ext_csd.hs_max_dtr) - max_dtr = card->ext_csd.hs_max_dtr; - if (mmc_card_highspeed(card) && (max_dtr > 52000000)) - max_dtr = 52000000; - } else if (max_dtr > card->csd.max_dtr) { - max_dtr = card->csd.max_dtr; - } - - mmc_set_clock(host, max_dtr); - - /* - * Indicate DDR mode (if supported). + * Select timing interface */ - if (mmc_card_highspeed(card)) { - if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) - && ((host->caps & (MMC_CAP_1_8V_DDR | - MMC_CAP_UHS_DDR50)) - == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) - ddr = MMC_1_8V_DDR_MODE; - else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) - && ((host->caps & (MMC_CAP_1_2V_DDR | - MMC_CAP_UHS_DDR50)) - == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) - ddr = MMC_1_2V_DDR_MODE; - } + err = mmc_select_timing(card); + if (err) + goto free_card; - /* - * Indicate HS200 SDR mode (if supported). - */ if (mmc_card_hs200(card)) { - u32 ext_csd_bits; - u32 bus_width = card->host->ios.bus_width; - - /* - * For devices supporting HS200 mode, the bus width has - * to be set before executing the tuning function. If - * set before tuning, then device will respond with CRC - * errors for responses on CMD line. So for HS200 the - * sequence will be - * 1. set bus width 4bit / 8 bit (1 bit not supported) - * 2. switch to HS200 mode - * 3. set the clock to > 52Mhz <=200MHz and - * 4. execute tuning for HS200 - */ - if ((host->caps2 & MMC_CAP2_HS200) && - card->host->ops->execute_tuning) { - mmc_host_clk_hold(card->host); - err = card->host->ops->execute_tuning(card->host, - MMC_SEND_TUNING_BLOCK_HS200); - mmc_host_clk_release(card->host); - } - if (err) { - pr_warning("%s: tuning execution failed\n", - mmc_hostname(card->host)); + err = mmc_hs200_tuning(card); + if (err) goto err; + } else if (mmc_card_highspeed(card)) { + /* Select the desired bus width optionally */ + err = mmc_select_bus_width(card); + if (!IS_ERR_VALUE(err)) { + err = mmc_select_hs_ddr(card, ext_csd); + if (err) + goto err; } - - ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? - EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; - err = mmc_select_powerclass(card, ext_csd_bits); - if (err) - pr_warning("%s: power class selection to bus width %d" - " failed\n", mmc_hostname(card->host), - 1 << bus_width); } /* - * Activate wide bus and DDR (if supported). + * Choose the power calss with selected bus interface */ - if (!mmc_card_hs200(card) && - (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && - (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { - static unsigned ext_csd_bits[][2] = { - { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, - { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, - { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, - }; - static unsigned bus_widths[] = { - MMC_BUS_WIDTH_8, - MMC_BUS_WIDTH_4, - MMC_BUS_WIDTH_1 - }; - unsigned idx, bus_width = 0; - - if (host->caps & MMC_CAP_8_BIT_DATA) - idx = 0; - else - idx = 1; - for (; idx < ARRAY_SIZE(bus_widths); idx++) { - bus_width = bus_widths[idx]; - if (bus_width == MMC_BUS_WIDTH_1) - ddr = 0; /* no DDR for 1-bit width */ - err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); - if (err) - pr_warning("%s: power class selection to " - "bus width %d failed\n", - mmc_hostname(card->host), - 1 << bus_width); - - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx][0], - card->ext_csd.generic_cmd6_time); - if (!err) { - mmc_set_bus_width(card->host, bus_width); - - /* - * If controller can't handle bus width test, - * compare ext_csd previously read in 1 bit mode - * against ext_csd at new bus width - */ - if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) - err = mmc_compare_ext_csds(card, - bus_width); - else - err = mmc_bus_test(card, bus_width); - if (!err) - break; - } - } - - if (!err && ddr) { - err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); - if (err) - pr_warning("%s: power class selection to " - "bus width %d ddr %d failed\n", - mmc_hostname(card->host), - 1 << bus_width, ddr); - - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx][1], - card->ext_csd.generic_cmd6_time); - } - if (err) { - pr_warning("%s: switch to bus width %d ddr %d " - "failed\n", mmc_hostname(card->host), - 1 << bus_width, ddr); - goto free_card; - } else if (ddr) { - /* - * eMMC cards can support 3.3V to 1.2V i/o (vccq) - * signaling. - * - * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. - * - * 1.8V vccq at 3.3V core voltage (vcc) is not required - * in the JEDEC spec for DDR. - * - * Do not force change in vccq since we are obviously - * working and no change to vccq is needed. - * - * WARNING: eMMC rules are NOT the same as SD DDR - */ - if (ddr == MMC_1_2V_DDR_MODE) { - err = __mmc_set_signal_voltage(host, - MMC_SIGNAL_VOLTAGE_120); - if (err) - goto err; - } - mmc_card_set_ddr_mode(card); - mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); - mmc_set_bus_width(card->host, bus_width); - } - } + mmc_select_powerclass(card, ext_csd); /* * Enable HPI feature (if supported) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 176fdf8..c119735 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -63,6 +63,7 @@ struct mmc_ext_csd { unsigned int power_off_longtime; /* Units: ms */ u8 power_off_notification; /* state */ unsigned int hs_max_dtr; + unsigned int hs200_max_dtr; #define MMC_HIGH_26_MAX_DTR 26000000 #define MMC_HIGH_52_MAX_DTR 52000000 #define MMC_HIGH_DDR_MAX_DTR 52000000 @@ -299,7 +300,10 @@ struct mmc_card { const char **info; /* info strings */ struct sdio_func_tuple *tuples; /* unknown common tuples */ - unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ + union { + unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ + unsigned int mmc_avail_type; /* supported device type by both host and card */ + }; struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 99f5709..69d58b1 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -60,12 +60,6 @@ struct mmc_ios { #define MMC_TIMING_UHS_DDR50 7 #define MMC_TIMING_MMC_HS200 8 -#define MMC_SDR_MODE 0 -#define MMC_1_2V_DDR_MODE 1 -#define MMC_1_8V_DDR_MODE 2 -#define MMC_1_2V_SDR_MODE 3 -#define MMC_1_8V_SDR_MODE 4 - unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ #define MMC_SIGNAL_VOLTAGE_330 0 diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 50bcde3..87df508 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -373,6 +373,10 @@ struct _mmc_csd { #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ + #define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_BD_BLK_EN BIT(2) #define EXT_CSD_SEC_GB_CL_EN BIT(4)
Current implementation for bus speed mode selection is too complicated. This patch is to simplify the codes and remove some duplicate parts. The following changes are including: * Adds functions for each mode selection(HS, HS-DDR, HS200 and etc) * Rearranged the mode selection sequence with supported device type * Power class is switched once only after bus modes(speed/width) are selected finally * Adds maximum speed for HS200 mode(hs200_max_dtr) * Adds available device type to be supported by both host and device * Adds field definition for HS_TIMING of EXT_CSD Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> --- drivers/mmc/core/mmc.c | 574 +++++++++++++++++++++++++++------------------- include/linux/mmc/card.h | 6 +- include/linux/mmc/host.h | 6 - include/linux/mmc/mmc.h | 4 + 4 files changed, 345 insertions(+), 245 deletions(-)