diff options
author | Pavan Kunapuli <pkunapuli@nvidia.com> | 2011-01-10 20:43:32 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-04-26 15:49:23 -0700 |
commit | 320395ccc6b474be0855c7323126fa62fb4ef143 (patch) | |
tree | 2d68f85ac753597a9f07fca291e7143ecce9d94c /drivers/mmc | |
parent | ff77b31c173918111e98cf7f9d704d94c5deb3c2 (diff) |
sd:Adding support for sd3.0
Adding support for sd3.0 cards. Added
support for SDR50, SDR104 and DDR50 modes.
Bug 661035
Original-Change-Id: Iecd6634b8e25d5fcbc05f79f34aea40a8460b527
Reviewed-on: http://git-master/r/15003
Tested-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-by: Venkata Nageswara Penumarty <vpenumarty@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Change-Id: Ic2119387d55b2e4263f50a606e0f957ab518f0cb
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 42 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 4 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 121 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.c | 59 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.h | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 81 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 10 |
7 files changed, 310 insertions, 9 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 175c1a039f74..0d0c0e1d4738 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -673,6 +673,48 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) mmc_set_ios(host); } +/* + * Switch the signalling voltage. + */ +void mmc_switch_signalling_voltage(struct mmc_host * host, unsigned int signalling_voltage) +{ + host->ios.signalling_voltage = signalling_voltage; + mmc_set_ios(host); +} + +/* + * Start the frequency tuning process. + */ +void mmc_start_tuning(struct mmc_host *host) +{ + host->ios.tuning_arg = MMC_EXECUTE_TUNING; + host->tuning_status = 0; + mmc_set_ios(host); + host->ios.tuning_arg = 0; +} + +/* + * Get the frequency tuning status. + */ +void mmc_get_tuning_status(struct mmc_host *host, int tuning_arg) +{ + host->ios.tuning_arg = tuning_arg; + host->tuning_status = 0; + mmc_set_ios(host); + host->ios.tuning_arg = 0; +} + +/* + * Reset the tuning circuit. + */ +void mmc_reset_tuning_circuit(struct mmc_host *host) +{ + host->ios.tuning_arg = MMC_RESET_TUNING_CIRCUIT; + host->tuning_status = 0; + mmc_set_ios(host); + host->ios.tuning_arg = 0; +} + /** * mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number * @vdd: voltage (mV) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 1df918567d4b..d341dc51b196 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -38,6 +38,10 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_bus_speed(struct mmc_host *host, unsigned char bus_speed_mode); +void mmc_switch_signalling_voltage(struct mmc_host *host, unsigned int signalling_voltage); +void mmc_start_tuning(struct mmc_host *host); +void mmc_get_tuning_status(struct mmc_host *host, int tuning_arg); +void mmc_reset_tuning_circuit(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7ab8fdc61c77..206b21f9797a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -250,6 +250,59 @@ out: return err; } +static int mmc_execute_tuning(struct mmc_card *card) +{ + int err; + u8 resp; + + mmc_start_tuning(card->host); + + err = mmc_send_tuning_pattern(card, &resp); + if (err) + goto out; + + mmc_get_tuning_status(card->host, MMC_QUERY_TUNING_STATUS); + if (card->host->tuning_status != MMC_SD_TUNING_COMPLETED) + err = -EAGAIN; +out: + card->host->tuning_status = 0; + return err; +} + +static int mmc_frequency_tuning(struct mmc_card *card) +{ + int err; + unsigned int count = 0; + unsigned char tuning_done = 0; + + while (!tuning_done) { + err = mmc_execute_tuning(card); + if (err) { + count++; + continue; + } else { + if (count >= 40) { + count = 0; + mmc_reset_tuning_circuit(card->host); + continue; + } else + tuning_done = 1; + } + } + + /* If the sampling clock select is set, tuning is successful */ + mmc_get_tuning_status(card->host, MMC_SAMPLING_CLOCK_SELECT); + if (card->host->tuning_status != MMC_SD_SAMPLING_CLOCK_SELECT_SET) { + printk(KERN_ERR "%s: frequency tuning failed.Reset tuning circuit\n", + mmc_hostname(card->host)); + + mmc_reset_tuning_circuit(card->host); + return -EIO; + } + + return 0; +} + /* * Fetches and decodes switch information */ @@ -294,7 +347,11 @@ static int mmc_read_switch(struct mmc_card *card) goto out; } - if (status[13] & 0x02) + if (status[13] & 0x08) /* UHS104 mode */ + card->sw_caps.hs_max_dtr = 208000000; + else if (status[13] & 0x04) /* UHS50 mode */ + card->sw_caps.hs_max_dtr = 104000000; + else if (status[13] & 0x02) /* high speed mode */ card->sw_caps.hs_max_dtr = 50000000; out: @@ -310,6 +367,7 @@ int mmc_sd_switch_hs(struct mmc_card *card) { int err; u8 *status; + u8 bus_speed; if (card->scr.sda_vsn < SCR_SPEC_VER_1) return 0; @@ -332,11 +390,16 @@ int mmc_sd_switch_hs(struct mmc_card *card) return -ENOMEM; } - err = mmc_sd_switch(card, 1, 0, 1, status); + if (card->bus_speed) + bus_speed = card->bus_speed; /* Set the supported UHS mode */ + else + bus_speed = 1; /* Set high speed mode */ + + err = mmc_sd_switch(card, 1, 0, bus_speed, status); if (err) goto out; - if ((status[16] & 0xF) != 1) { + if ((status[16] & 0xF) != bus_speed) { printk(KERN_WARNING "%s: Problem switching card " "into high-speed mode!\n", mmc_hostname(card->host)); @@ -402,6 +465,7 @@ struct device_type sd_type = { int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) { int err; + u32 rocr; /* * Since we're changing the OCR value, we seem to @@ -421,10 +485,30 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) if (!err) ocr |= 1 << 30; - err = mmc_send_app_op_cond(host, ocr, NULL); + /* + * Check the voltage switching capability if the host supports SDR + * or DDR modes. + */ + if (((host->caps & MMC_CAP_SDR50) || (host->caps & MMC_CAP_SDR104) || + (host->caps & MMC_CAP_DDR50))&& (!mmc_host_is_spi(host))) + ocr |= (1 << 24); + + err = mmc_send_app_op_cond(host, ocr, &rocr); if (err) return err; + /* Check if switch to 1.8V is accepted by the card */ + if ((rocr >> 24) & 1) { + /* Issue voltage switch command */ + err = mmc_send_voltage_switch(host); + if (err) + return err; + + /* Switch to 1.8 V */ + mmc_switch_signalling_voltage(host, MMC_1_8_VOLT_SIGNALLING); + host->is_voltage_switched = 1; + } + if (mmc_host_is_spi(host)) err = mmc_send_cid(host, cid); else @@ -560,6 +644,8 @@ void mmc_sd_go_highspeed(struct mmc_card *card) { mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_SD_HS); + if (card->bus_speed) + mmc_set_bus_speed(card->host, card->bus_speed); } /* @@ -632,6 +718,23 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, goto free_card; /* + * If voltage switching is supported, attempt to switch to a supported + * UHS mode. If voltage switching is not supported, attempt to switch + * to high speed mode. + */ + if (host->is_voltage_switched) { + if ((host->caps & MMC_CAP_SDR104) && + (card->sw_caps.hs_max_dtr == 208000000)) { + card->bus_speed = MMC_BUS_SPEED_MODE_SDR104; + } else if (host->caps & MMC_CAP_SDR50) { + card->bus_speed = MMC_BUS_SPEED_MODE_SDR50; + } else if (host->caps & MMC_CAP_DDR50) { + card->bus_speed = MMC_BUS_SPEED_MODE_DDR50; + } else + card->bus_speed = 0; + } + + /* * Attempt to change to high-speed (if supported) */ err = mmc_sd_switch_hs(card); @@ -646,6 +749,16 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, mmc_set_clock(host, mmc_sd_get_max_clock(card)); /* + * If SDR104 or SDR50 modes are set, do the frequency tuning. + */ + if ((card->bus_speed == MMC_BUS_SPEED_MODE_SDR104) || + (card->bus_speed == MMC_BUS_SPEED_MODE_SDR50)){ + err = mmc_frequency_tuning(card); + if (err) + goto free_card; + } + + /* * Switch to wider bus (if supported). */ if ((host->caps & MMC_CAP_4_BIT_DATA) && diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 797cdb5887fd..c450af8276d8 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -394,3 +394,62 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) return 0; } + +int mmc_send_voltage_switch(struct mmc_host *host) +{ + int err = 0; + struct mmc_command cmd = { + .opcode = SD_VOLTAGE_SWITCH, + .arg = 0, + .flags = MMC_RSP_R1 | MMC_CMD_AC, + }; + + BUG_ON(!host); + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); + + return err; +} + +int mmc_send_tuning_pattern(struct mmc_card *card, u8 *resp) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(!card->host); + + /* NOTE: caller guarantees resp is heap-allocated */ + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_SEND_TUNING_PATTERN; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, resp, 64); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + return 0; +} + diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h index ffc2305d905f..17daf7842485 100644 --- a/drivers/mmc/core/sd_ops.h +++ b/drivers/mmc/core/sd_ops.h @@ -20,6 +20,8 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr); int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp); int mmc_app_sd_status(struct mmc_card *card, void *ssr); +int mmc_send_voltage_switch(struct mmc_host *host); +int mmc_send_tuning_pattern(struct mmc_card *card, u8 *resp); #endif diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c7bc61fd13ca..4ea522ba5044 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -179,6 +179,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) if (mask & SDHCI_RESET_ALL) host->ops->configure_capabilities(host); + host->uhs_mode = 0; } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); @@ -673,8 +674,13 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) count = sdhci_calc_timeout(host, data); sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) - host->flags |= SDHCI_REQ_USE_DMA; + /* Frequency tuning uses only PIO mode */ + if (host->mmc->tuning_status == MMC_SD_TUNING_IN_PROGRESS) { + host->flags &= ~SDHCI_REQ_USE_DMA; + } else { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) + host->flags |= SDHCI_REQ_USE_DMA; + } /* * FIXME: This doesn't account for merging when mapping the @@ -1217,6 +1223,14 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl |= SDHCI_UHS_MODE_SEL_DDR50; host->uhs_mode = SDHCI_UHS_MODE_SEL_DDR50; break; + case MMC_BUS_SPEED_MODE_SDR50: + ctrl |= SDHCI_UHS_MODE_SEL_SDR50; + host->uhs_mode = SDHCI_UHS_MODE_SEL_SDR50; + break; + case MMC_BUS_SPEED_MODE_SDR104: + ctrl |= SDHCI_UHS_MODE_SEL_SDR104; + host->uhs_mode = SDHCI_UHS_MODE_SEL_SDR104; + break; default: goto out; } @@ -1242,6 +1256,54 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } + if ((ios->signalling_voltage == MMC_1_8_VOLT_SIGNALLING) && + (ios->signalling_voltage != host->signalling_voltage)) { + /* Switch OFF SD clock */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= (~SDHCI_CLOCK_CARD_EN); + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL_2); + ctrl |= SDHCI_CTRL_2_VOLT_18_EN; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL_2); + + /* Wait for 5msec for the output to be stable */ + mdelay(5); + + /* Switch ON sd clock */ + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + host->signalling_voltage = ios->signalling_voltage; + } + + if (ios->tuning_arg) { + switch(ios->tuning_arg) { + case MMC_EXECUTE_TUNING: + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL_2); + if (!(ctrl & SDHCI_CTRL_2_EXECUTE_TUNING)) { + ctrl |= SDHCI_CTRL_2_EXECUTE_TUNING; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL_2); + } + mmc->tuning_status = MMC_SD_TUNING_IN_PROGRESS; + break; + case MMC_QUERY_TUNING_STATUS: + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL_2); + if (!(ctrl & SDHCI_CTRL_2_EXECUTE_TUNING)) + mmc->tuning_status = MMC_SD_TUNING_COMPLETED; + break; + case MMC_SAMPLING_CLOCK_SELECT: + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL_2); + if (ctrl & SDHCI_CTRL_2_SAMPLING_CLOCK_SELECT) + mmc->tuning_status = MMC_SD_SAMPLING_CLOCK_SELECT_SET; + break; + default: + printk(KERN_ERR "%s: Undefined tuning operation\n", + mmc_hostname(host->mmc)); + break; + } + } + out: mmiowb(); spin_unlock_irqrestore(&host->lock, flags); @@ -1591,8 +1653,12 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (host->data->error) sdhci_finish_data(host); else { - if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) - sdhci_transfer_pio(host); + if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) { + if (host->mmc->tuning_status == MMC_SD_TUNING_IN_PROGRESS) + tasklet_schedule(&host->finish_tasklet); + else + sdhci_transfer_pio(host); + } /* * We currently don't do anything fancy with DMA @@ -2026,7 +2092,12 @@ int sdhci_add_host(struct sdhci_host *host) caps = sdhci_readb(host, SDHCI_HIGHER_CAPABILITIES); if (caps & SDHCI_CAN_SUPPORT_DDR50) mmc->caps |= MMC_CAP_DDR50; - + if (caps & SDHCI_CAN_SUPPORT_SDR50) + mmc->caps |= MMC_CAP_SDR50; + if (caps & SDHCI_CAN_SUPPORT_SDR104) + mmc->caps |= MMC_CAP_SDR104; + if (caps & SDHCI_CAN_SDR50_TUNING) + mmc->caps |= MMC_CAP_SDR50_TUNING; /* * Init tasklets. diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index dc346f14664c..69576e84fa3d 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -135,7 +135,13 @@ #define SDHCI_ACMD12_ERR 0x3C #define SDHCI_HOST_CONTROL_2 0x3E +#define SDHCI_UHS_MODE_SEL_SDR25 0x01 +#define SDHCI_UHS_MODE_SEL_SDR50 0x02 +#define SDHCI_UHS_MODE_SEL_SDR104 0x03 #define SDHCI_UHS_MODE_SEL_DDR50 0x04 +#define SDHCI_CTRL_2_VOLT_18_EN 0x08 +#define SDHCI_CTRL_2_EXECUTE_TUNING 0x40 +#define SDHCI_CTRL_2_SAMPLING_CLOCK_SELECT 0x80 #define SDHCI_CAPABILITIES 0x40 #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F @@ -155,7 +161,10 @@ #define SDHCI_CAN_64BIT 0x10000000 #define SDHCI_HIGHER_CAPABILITIES 0x44 +#define SDHCI_CAN_SUPPORT_SDR50 0x00000001 +#define SDHCI_CAN_SUPPORT_SDR104 0x00000002 #define SDHCI_CAN_SUPPORT_DDR50 0x00000004 +#define SDHCI_CAN_SDR50_TUNING 0x00002000 #define SDHCI_MAX_CURRENT 0x48 @@ -301,6 +310,7 @@ struct sdhci_host { unsigned int clock; /* Current clock (MHz) */ u8 pwr; /* Current voltage */ u8 uhs_mode; /* Current uhs mode */ + u8 signalling_voltage; /* Signalling voltage */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ |