mmc: implement Driver Stage Register handling
Some eMMC and SD cards implement a DSR register that allows to tune raise/fall times and drive strength of the CMD and DATA outputs. The values to use depend on the card in use and the host. It might be needed to reduce the drive strength to prevent voltage peaks above the host's specification. Implement a 'dsr' devicetree property that allows to specify the value to set the DSR to. For non-dt setups the new members of mmc_host can be set by board code. This patch was initially authored by Sascha Hauer. It contains improvements authored by Markus Niebel and Uwe Kleine-König. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Markus Niebel <Markus.Niebel@tq-group.com> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
b315376573
commit
3d705d14fe
|
@ -40,6 +40,8 @@ Optional properties:
|
||||||
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
|
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
|
||||||
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
|
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
|
||||||
- mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
|
- mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
|
||||||
|
- dsr: Value the card's (optional) Driver Stage Register (DSR) should be
|
||||||
|
programmed with. Valid range: [0 .. 0xffff].
|
||||||
|
|
||||||
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
||||||
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
||||||
|
|
|
@ -452,6 +452,14 @@ int mmc_of_parse(struct mmc_host *host)
|
||||||
if (of_find_property(np, "mmc-hs400-1_2v", &len))
|
if (of_find_property(np, "mmc-hs400-1_2v", &len))
|
||||||
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
|
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
|
||||||
|
|
||||||
|
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
|
||||||
|
if (host->dsr_req && (host->dsr & ~0xffff)) {
|
||||||
|
dev_err(host->parent,
|
||||||
|
"device tree specified broken value for DSR: 0x%x, ignoring\n",
|
||||||
|
host->dsr);
|
||||||
|
host->dsr_req = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -162,6 +162,7 @@ static int mmc_decode_csd(struct mmc_card *card)
|
||||||
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
|
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
|
||||||
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
|
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
|
||||||
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
|
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
|
||||||
|
csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
|
||||||
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
|
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
|
||||||
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
|
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
|
||||||
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
|
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
|
||||||
|
@ -1271,6 +1272,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
goto free_card;
|
goto free_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handling only for cards supporting DSR and hosts requesting
|
||||||
|
* DSR configuration
|
||||||
|
*/
|
||||||
|
if (card->csd.dsr_imp && host->dsr_req)
|
||||||
|
mmc_set_dsr(host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select card, as all following commands rely on that.
|
* Select card, as all following commands rely on that.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -93,6 +93,26 @@ int mmc_deselect_cards(struct mmc_host *host)
|
||||||
return _mmc_select_card(host, NULL);
|
return _mmc_select_card(host, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write the value specified in the device tree or board code into the optional
|
||||||
|
* 16 bit Driver Stage Register. This can be used to tune raise/fall times and
|
||||||
|
* drive strength of the DAT and CMD outputs. The actual meaning of a given
|
||||||
|
* value is hardware dependant.
|
||||||
|
* The presence of the DSR register can be determined from the CSD register,
|
||||||
|
* bit 76.
|
||||||
|
*/
|
||||||
|
int mmc_set_dsr(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
struct mmc_command cmd = {0};
|
||||||
|
|
||||||
|
cmd.opcode = MMC_SET_DSR;
|
||||||
|
|
||||||
|
cmd.arg = (host->dsr << 16) | 0xffff;
|
||||||
|
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
|
||||||
|
|
||||||
|
return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||||
|
}
|
||||||
|
|
||||||
int mmc_go_idle(struct mmc_host *host)
|
int mmc_go_idle(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
int mmc_select_card(struct mmc_card *card);
|
int mmc_select_card(struct mmc_card *card);
|
||||||
int mmc_deselect_cards(struct mmc_host *host);
|
int mmc_deselect_cards(struct mmc_host *host);
|
||||||
|
int mmc_set_dsr(struct mmc_host *host);
|
||||||
int mmc_go_idle(struct mmc_host *host);
|
int mmc_go_idle(struct mmc_host *host);
|
||||||
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
||||||
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
|
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
|
||||||
|
|
|
@ -127,6 +127,7 @@ static int mmc_decode_csd(struct mmc_card *card)
|
||||||
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
|
csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
|
||||||
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
|
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
|
||||||
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
|
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
|
||||||
|
csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
|
||||||
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
|
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
|
||||||
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
|
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
|
||||||
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
|
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
|
||||||
|
@ -953,6 +954,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||||
mmc_decode_cid(card);
|
mmc_decode_cid(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handling only for cards supporting DSR and hosts requesting
|
||||||
|
* DSR configuration
|
||||||
|
*/
|
||||||
|
if (card->csd.dsr_imp && host->dsr_req)
|
||||||
|
mmc_set_dsr(host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select card, as all following commands rely on that.
|
* Select card, as all following commands rely on that.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -42,7 +42,8 @@ struct mmc_csd {
|
||||||
unsigned int read_partial:1,
|
unsigned int read_partial:1,
|
||||||
read_misalign:1,
|
read_misalign:1,
|
||||||
write_partial:1,
|
write_partial:1,
|
||||||
write_misalign:1;
|
write_misalign:1,
|
||||||
|
dsr_imp:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mmc_ext_csd {
|
struct mmc_ext_csd {
|
||||||
|
|
|
@ -365,6 +365,9 @@ struct mmc_host {
|
||||||
|
|
||||||
unsigned int slotno; /* used for sdio acpi binding */
|
unsigned int slotno; /* used for sdio acpi binding */
|
||||||
|
|
||||||
|
int dsr_req; /* DSR value is valid */
|
||||||
|
u32 dsr; /* optional driver stage (DSR) value */
|
||||||
|
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue