Commit a303c531 authored by Philip Rakity's avatar Philip Rakity Committed by Chris Ball

mmc: sdio: support SDIO UHS cards

This patch adds support for sdio UHS cards per the version 3.0
spec.

UHS mode is only enabled for version 3.0 cards when both the
host and the controller support UHS modes.

1.8v signaling support is removed if both the card and the
host do not support UHS.  This is done to maintain
compatibility and some system/card combinations break when
1.8v signaling is enabled when the host does not support UHS.
Signed-off-by: default avatarPhilip Rakity <prakity@marvell.com>
Signed-off-by: default avatarAaron Lu <Aaron.lu@amd.com>
Reviewed-by: default avatarArindam Nath <arindam.nath@amd.com>
Tested-by: default avatarBing Zhao <bzhao@marvell.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent b70a7fab
...@@ -305,7 +305,7 @@ int mmc_add_card(struct mmc_card *card) ...@@ -305,7 +305,7 @@ int mmc_add_card(struct mmc_card *card)
} else { } else {
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n", printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
mmc_hostname(card->host), mmc_hostname(card->host),
mmc_sd_card_uhs(card) ? "ultra high speed " : mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""), (mmc_card_highspeed(card) ? "high speed " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "", mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca); type, card->rca);
......
...@@ -960,7 +960,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -960,7 +960,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
goto free_card; goto free_card;
/* Card is an ultra-high-speed card */ /* Card is an ultra-high-speed card */
mmc_sd_card_set_uhs(card); mmc_card_set_uhs(card);
/* /*
* Since initialization is now complete, enable preset * Since initialization is now complete, enable preset
......
...@@ -102,6 +102,7 @@ static int sdio_read_cccr(struct mmc_card *card) ...@@ -102,6 +102,7 @@ static int sdio_read_cccr(struct mmc_card *card)
int ret; int ret;
int cccr_vsn; int cccr_vsn;
unsigned char data; unsigned char data;
unsigned char speed;
memset(&card->cccr, 0, sizeof(struct sdio_cccr)); memset(&card->cccr, 0, sizeof(struct sdio_cccr));
...@@ -140,12 +141,60 @@ static int sdio_read_cccr(struct mmc_card *card) ...@@ -140,12 +141,60 @@ static int sdio_read_cccr(struct mmc_card *card)
} }
if (cccr_vsn >= SDIO_CCCR_REV_1_20) { if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data); ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
if (ret) if (ret)
goto out; goto out;
if (data & SDIO_SPEED_SHS) card->scr.sda_spec3 = 0;
card->cccr.high_speed = 1; card->sw_caps.sd3_bus_mode = 0;
card->sw_caps.sd3_drv_type = 0;
if (cccr_vsn >= SDIO_CCCR_REV_3_00) {
card->scr.sda_spec3 = 1;
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_CCCR_UHS, 0, &data);
if (ret)
goto out;
if (card->host->caps &
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_DDR50)) {
if (data & SDIO_UHS_DDR50)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_DDR50;
if (data & SDIO_UHS_SDR50)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_SDR50;
if (data & SDIO_UHS_SDR104)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_SDR104;
}
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_CCCR_DRIVE_STRENGTH, 0, &data);
if (ret)
goto out;
if (data & SDIO_DRIVE_SDTA)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_A;
if (data & SDIO_DRIVE_SDTC)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
if (data & SDIO_DRIVE_SDTD)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
}
/* if no uhs mode ensure we check for high speed */
if (!card->sw_caps.sd3_bus_mode) {
if (speed & SDIO_SPEED_SHS) {
card->cccr.high_speed = 1;
card->sw_caps.hs_max_dtr = 50000000;
} else {
card->cccr.high_speed = 0;
card->sw_caps.hs_max_dtr = 25000000;
}
}
} }
out: out:
...@@ -327,6 +376,193 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) ...@@ -327,6 +376,193 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
return max_dtr; return max_dtr;
} }
static unsigned char host_drive_to_sdio_drive(int host_strength)
{
switch (host_strength) {
case MMC_SET_DRIVER_TYPE_A:
return SDIO_DTSx_SET_TYPE_A;
case MMC_SET_DRIVER_TYPE_B:
return SDIO_DTSx_SET_TYPE_B;
case MMC_SET_DRIVER_TYPE_C:
return SDIO_DTSx_SET_TYPE_C;
case MMC_SET_DRIVER_TYPE_D:
return SDIO_DTSx_SET_TYPE_D;
default:
return SDIO_DTSx_SET_TYPE_B;
}
}
static void sdio_select_driver_type(struct mmc_card *card)
{
int host_drv_type = SD_DRIVER_TYPE_B;
int card_drv_type = SD_DRIVER_TYPE_B;
int drive_strength;
unsigned char card_strength;
int err;
/*
* If the host doesn't support any of the Driver Types A,C or D,
* or there is no board specific handler then default Driver
* Type B is used.
*/
if (!(card->host->caps &
(MMC_CAP_DRIVER_TYPE_A |
MMC_CAP_DRIVER_TYPE_C |
MMC_CAP_DRIVER_TYPE_D)))
return;
if (!card->host->ops->select_drive_strength)
return;
if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
host_drv_type |= SD_DRIVER_TYPE_A;
if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
host_drv_type |= SD_DRIVER_TYPE_C;
if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
host_drv_type |= SD_DRIVER_TYPE_D;
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
card_drv_type |= SD_DRIVER_TYPE_A;
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
card_drv_type |= SD_DRIVER_TYPE_C;
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
card_drv_type |= SD_DRIVER_TYPE_D;
/*
* The drive strength that the hardware can support
* depends on the board design. Pass the appropriate
* information and let the hardware specific code
* return what is possible given the options
*/
drive_strength = card->host->ops->select_drive_strength(
card->sw_caps.uhs_max_dtr,
host_drv_type, card_drv_type);
/* if error just use default for drive strength B */
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
&card_strength);
if (err)
return;
card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
card_strength |= host_drive_to_sdio_drive(drive_strength);
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
card_strength, NULL);
/* if error default to drive strength B */
if (!err)
mmc_set_driver_type(card->host, drive_strength);
}
static int sdio_set_bus_speed_mode(struct mmc_card *card)
{
unsigned int bus_speed, timing;
int err;
unsigned char speed;
/*
* If the host doesn't support any of the UHS-I modes, fallback on
* default speed.
*/
if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
return 0;
bus_speed = SDIO_SPEED_SDR12;
timing = MMC_TIMING_UHS_SDR12;
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
bus_speed = SDIO_SPEED_SDR104;
timing = MMC_TIMING_UHS_SDR104;
card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
bus_speed = SDIO_SPEED_DDR50;
timing = MMC_TIMING_UHS_DDR50;
card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR50)) {
bus_speed = SDIO_SPEED_SDR50;
timing = MMC_TIMING_UHS_SDR50;
card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
bus_speed = SDIO_SPEED_SDR25;
timing = MMC_TIMING_UHS_SDR25;
card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR12)) {
bus_speed = SDIO_SPEED_SDR12;
timing = MMC_TIMING_UHS_SDR12;
card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
}
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
if (err)
return err;
speed &= ~SDIO_SPEED_BSS_MASK;
speed |= bus_speed;
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (err)
return err;
if (bus_speed) {
mmc_set_timing(card->host, timing);
mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
}
return 0;
}
/*
* UHS-I specific initialization procedure
*/
static int mmc_sdio_init_uhs_card(struct mmc_card *card)
{
int err;
if (!card->scr.sda_spec3)
return 0;
/*
* Switch to wider bus (if supported).
*/
if (card->host->caps & MMC_CAP_4_BIT_DATA) {
err = sdio_enable_4bit_bus(card);
if (err > 0) {
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
err = 0;
}
}
/* Set the driver strength for the card */
sdio_select_driver_type(card);
/* Set bus speed mode of the card */
err = sdio_set_bus_speed_mode(card);
if (err)
goto out;
/* Initialize and start re-tuning timer */
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
err = card->host->ops->execute_tuning(card->host);
out:
return err;
}
/* /*
* Handle the detection and initialisation of a card. * Handle the detection and initialisation of a card.
* *
...@@ -393,6 +629,30 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -393,6 +629,30 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
if (host->ops->init_card) if (host->ops->init_card)
host->ops->init_card(host, card); host->ops->init_card(host, card);
/*
* If the host and card support UHS-I mode request the card
* to switch to 1.8V signaling level. No 1.8v signalling if
* UHS mode is not enabled to maintain compatibilty and some
* systems that claim 1.8v signalling in fact do not support
* it.
*/
if ((ocr & R4_18V_PRESENT) &&
(host->caps &
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_DDR50))) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
true);
if (err) {
ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
}
err = 0;
} else {
ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
}
/* /*
* For native busses: set card RCA and quit open drain mode. * For native busses: set card RCA and quit open drain mode.
*/ */
...@@ -492,29 +752,39 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -492,29 +752,39 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
if (err) if (err)
goto remove; goto remove;
/* /* Initialization sequence for UHS-I cards */
* Switch to high-speed (if supported). /* Only if card supports 1.8v and UHS signaling */
*/ if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {
err = sdio_enable_hs(card); err = mmc_sdio_init_uhs_card(card);
if (err > 0) if (err)
mmc_sd_go_highspeed(card); goto remove;
else if (err)
goto remove;
/* /* Card is an ultra-high-speed card */
* Change to the card's maximum speed. mmc_card_set_uhs(card);
*/ } else {
mmc_set_clock(host, mmc_sdio_get_max_clock(card)); /*
* Switch to high-speed (if supported).
*/
err = sdio_enable_hs(card);
if (err > 0)
mmc_sd_go_highspeed(card);
else if (err)
goto remove;
/* /*
* Switch to wider bus (if supported). * Change to the card's maximum speed.
*/ */
err = sdio_enable_4bit_bus(card); mmc_set_clock(host, mmc_sdio_get_max_clock(card));
if (err > 0)
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
else if (err)
goto remove;
/*
* Switch to wider bus (if supported).
*/
err = sdio_enable_4bit_bus(card);
if (err > 0)
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
else if (err)
goto remove;
}
finish: finish:
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
...@@ -797,8 +1067,17 @@ int mmc_attach_sdio(struct mmc_host *host) ...@@ -797,8 +1067,17 @@ int mmc_attach_sdio(struct mmc_host *host)
* Detect and init the card. * Detect and init the card.
*/ */
err = mmc_sdio_init_card(host, host->ocr, NULL, 0); err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
if (err) if (err) {
goto err; if (err == -EAGAIN) {
/*
* Retry initialization with S18R set to 0.
*/
host->ocr &= ~R4_18V_PRESENT;
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
}
if (err)
goto err;
}
card = host->card; card = host->card;
/* /*
......
...@@ -367,7 +367,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) ...@@ -367,7 +367,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
...@@ -375,6 +376,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) ...@@ -375,6 +376,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
* [8:0] Byte/block count * [8:0] Byte/block count
*/ */
#define R4_18V_PRESENT (1<<24)
#define R4_MEMORY_PRESENT (1 << 27) #define R4_MEMORY_PRESENT (1 << 27)
/* /*
...@@ -85,6 +86,7 @@ ...@@ -85,6 +86,7 @@
#define SDIO_SD_REV_1_01 0 /* SD Physical Spec Version 1.01 */ #define SDIO_SD_REV_1_01 0 /* SD Physical Spec Version 1.01 */
#define SDIO_SD_REV_1_10 1 /* SD Physical Spec Version 1.10 */ #define SDIO_SD_REV_1_10 1 /* SD Physical Spec Version 1.10 */
#define SDIO_SD_REV_2_00 2 /* SD Physical Spec Version 2.00 */ #define SDIO_SD_REV_2_00 2 /* SD Physical Spec Version 2.00 */
#define SDIO_SD_REV_3_00 3 /* SD Physical Spev Version 3.00 */
#define SDIO_CCCR_IOEx 0x02 #define SDIO_CCCR_IOEx 0x02
#define SDIO_CCCR_IORx 0x03 #define SDIO_CCCR_IORx 0x03
...@@ -134,8 +136,31 @@ ...@@ -134,8 +136,31 @@
#define SDIO_CCCR_SPEED 0x13 #define SDIO_CCCR_SPEED 0x13
#define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */ #define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */
#define SDIO_SPEED_EHS 0x02 /* Enable High-Speed mode */ #define SDIO_SPEED_BSS_SHIFT 1
#define SDIO_SPEED_BSS_MASK (7<<SDIO_SPEED_BSS_SHIFT)
#define SDIO_SPEED_SDR12 (0<<SDIO_SPEED_BSS_SHIFT)
#define SDIO_SPEED_SDR25 (1<<SDIO_SPEED_BSS_SHIFT)
#define SDIO_SPEED_SDR50 (2<<SDIO_SPEED_BSS_SHIFT)
#define SDIO_SPEED_SDR104 (3<<SDIO_SPEED_BSS_SHIFT)
#define SDIO_SPEED_DDR50 (4<<SDIO_SPEED_BSS_SHIFT)
#define SDIO_SPEED_EHS SDIO_SPEED_SDR25 /* Enable High-Speed */
#define SDIO_CCCR_UHS 0x14
#define SDIO_UHS_SDR50 0x01
#define SDIO_UHS_SDR104 0x02
#define SDIO_UHS_DDR50 0x04
#define SDIO_CCCR_DRIVE_STRENGTH 0x15
#define SDIO_SDTx_MASK 0x07
#define SDIO_DRIVE_SDTA (1<<0)
#define SDIO_DRIVE_SDTC (1<<1)
#define SDIO_DRIVE_SDTD (1<<2)
#define SDIO_DRIVE_DTSx_MASK 0x03
#define SDIO_DRIVE_DTSx_SHIFT 4
#define SDIO_DTSx_SET_TYPE_B (0 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT)
/* /*
* Function Basic Registers (FBR) * Function Basic Registers (FBR)
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment