From 0cd2f04453fcb7bf3f38a4a72d1619636b4afa57 Mon Sep 17 00:00:00 2001 From: Michele Curti Date: Sat, 5 Sep 2015 08:49:52 +0200 Subject: mmc: sdhci-acpi: detect sd card reader on asus x205ta Add an entry to the sdhci_acpi_uids list to detect the SD card reader on the Asus X205Ta laptop. dstd table: Device (SDHC) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "PNP0FFF") // _HID: Hardware ID Name (_CID, "PNP0D40" /* SDA Standard Compliant SD Host Controller */) Name (_DDN, "Intel(R) SD Card Controller - 80860F16") // _DDN: DOS Dev Name (_UID, 0x03) // _UID: Unique ID Name (RDEP, Package (0x02) Signed-off-by: Michele Curti Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 22d929f..78aa16a 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -247,6 +247,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = { { "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio }, { "INT3436" , NULL, &sdhci_acpi_slot_int_sdio }, { "INT344D" , NULL, &sdhci_acpi_slot_int_sdio }, + { "PNP0FFF" , "3" , &sdhci_acpi_slot_int_sd }, { "PNP0D40" }, { }, }; -- cgit v0.10.2 From 8d1ffc8c982e4480059ef735e8f15f631e40e80c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 11 Sep 2015 14:41:55 +0200 Subject: mmc: core: Keep host claimed while invoking mmc_power_off|up() As mmc_claim_host() invokes pm_runtime_get_sync() for the mmc host device, it's important that the host is kept claimed for *all* accesses to it via the host_ops callbacks. In some code paths for SDIO, particularly related to the PM support, mmc_power_off|up() is invoked without keeping the host claimed. Let's fix these. Moreover, mmc_start|stop_host() also invokes mmc_power_off|up() without claiming the host, let's fix these as well. Signed-off-by: Ulf Hansson Acked-by: Kishon Vijay Abraham I diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a3eb20b..2092a89 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2633,10 +2633,14 @@ void mmc_start_host(struct mmc_host *host) host->f_init = max(freqs[0], host->f_min); host->rescan_disable = 0; host->ios.power_mode = MMC_POWER_UNDEFINED; + + mmc_claim_host(host); if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else mmc_power_up(host, host->ocr_avail); + mmc_release_host(host); + mmc_gpiod_request_cd_irq(host); _mmc_detect_change(host, 0, false); } @@ -2674,7 +2678,9 @@ void mmc_stop_host(struct mmc_host *host) BUG_ON(host->card); + mmc_claim_host(host); mmc_power_off(host); + mmc_release_host(host); } int mmc_power_save_host(struct mmc_host *host) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index b91abed..95bc101 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -897,11 +897,10 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host) */ static int mmc_sdio_suspend(struct mmc_host *host) { - if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { - mmc_claim_host(host); + mmc_claim_host(host); + + if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) sdio_disable_wide(host->card); - mmc_release_host(host); - } if (!mmc_card_keep_power(host)) { mmc_power_off(host); @@ -910,6 +909,8 @@ static int mmc_sdio_suspend(struct mmc_host *host) mmc_retune_needed(host); } + mmc_release_host(host); + return 0; } @@ -1018,15 +1019,24 @@ out: static int mmc_sdio_runtime_suspend(struct mmc_host *host) { /* No references to the card, cut the power to it. */ + mmc_claim_host(host); mmc_power_off(host); + mmc_release_host(host); + return 0; } static int mmc_sdio_runtime_resume(struct mmc_host *host) { + int ret; + /* Restore power and re-initialize. */ + mmc_claim_host(host); mmc_power_up(host, host->card->ocr); - return mmc_sdio_power_restore(host); + ret = mmc_sdio_power_restore(host); + mmc_release_host(host); + + return ret; } static int mmc_sdio_reset(struct mmc_host *host) -- cgit v0.10.2 From bf8b8511af7721e6f81848a1fd1359716602f3b7 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 10 Sep 2015 00:33:16 +0800 Subject: mmc: sd: Remove superfluous error code assignment Signed-off-by: Yousong Zhou Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 4e7366a..e28ebf3 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -357,8 +357,6 @@ int mmc_sd_switch_hs(struct mmc_card *card) if (card->sw_caps.hs_max_dtr == 0) return 0; - err = -EIO; - status = kmalloc(64, GFP_KERNEL); if (!status) { pr_err("%s: could not allocate a buffer for " -- cgit v0.10.2 From 17a90539710d090d7bfb736c071a94451dc09170 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 16 Sep 2015 11:30:03 +0200 Subject: mmc: wbsd: Remove unneded semicolon It's not needed an is just creating a null statement, so remove it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index ca183ea..c3fd16d 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -809,7 +809,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) cmd->error = -EINVAL; goto done; - }; + } } /* -- cgit v0.10.2 From 7eece8d0e436e1eabc6b0adb04872964afe20b35 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 16 Sep 2015 11:30:04 +0200 Subject: mmc: vub300: Remove unneded semicolons They aren't needed and are just creating null statements so remove it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index fbabbb8..1e819f9 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -563,7 +563,7 @@ static void add_offloaded_reg(struct vub300_mmc_host *vub300, i += 1; continue; } - }; + } __add_offloaded_reg_to_fifo(vub300, register_access, func); } @@ -1372,7 +1372,7 @@ static void download_offload_pseudocode(struct vub300_mmc_host *vub300) l += snprintf(vub300->vub_name + l, sizeof(vub300->vub_name) - l, "_%04X%04X", sf->vendor, sf->device); - }; + } snprintf(vub300->vub_name + l, sizeof(vub300->vub_name) - l, ".bin"); dev_info(&vub300->udev->dev, "requesting offload firmware %s\n", vub300->vub_name); @@ -1893,7 +1893,7 @@ static int satisfy_request_from_offloaded_data(struct vub300_mmc_host *vub300, i += 1; continue; } - }; + } if (vub300->total_offload_count == 0) return 0; else if (vub300->fn[func].offload_count == 0) -- cgit v0.10.2 From f90d2e4035d456cb20c0b784725d556eb4de4d8a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 15 Sep 2015 15:19:45 +0200 Subject: mmc: core: Convert __mmc_switch() into an internal core function As there are no users of the __mmc_switch() API, except for the mmc core itself, let's convert it from an exported function into an internal. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 0e9ae1c..57f3a21 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -579,7 +579,6 @@ out: return err; } -EXPORT_SYMBOL_GPL(__mmc_switch); int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms) diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index f498f9a..f1b8e81 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -28,6 +28,9 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); int mmc_can_ext_csd(struct mmc_card *card); int mmc_switch_status_error(struct mmc_host *host, u32 status); +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, + unsigned int timeout_ms, bool use_busy_signal, bool send_status, + bool ignore_crc); #endif diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 258daf9..79a31d3 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -152,8 +152,6 @@ extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); -extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, - bool, bool); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); extern int mmc_send_tuning(struct mmc_host *host); extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); -- cgit v0.10.2 From a30b55119d086d33bd6046dfdd4723c810fd2579 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 14 Sep 2015 14:00:35 +0200 Subject: mmc: pwrseq: use gpiod_get() instead of index 0 The gpiod_get() function expands to gpiod_get_index() with index 0 so it's better to use it since is easier to read and more concise. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c index 9d6d2fb..137c97f 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -76,7 +76,7 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, if (!pwrseq) return ERR_PTR(-ENOMEM); - pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); + pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(pwrseq->reset_gpio)) { ret = PTR_ERR(pwrseq->reset_gpio); goto free; -- cgit v0.10.2 From 28804293b9086100ae784a15d62ebf6249db433c Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 16 Sep 2015 11:53:20 +0200 Subject: mmc: sdhci-bcm-kona: fix logic to check for 8-bit data width The driver prints if the data width is 8-bit but it's using a binary OR instead of a binary AND so it will always report as "is_8bit=Y" regardless of the flags in host->mmc->caps. Signed-off-by: Javier Martinez Canillas Reviewed-by: Ray Jui Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 2bd90fb..00a8a40 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -273,7 +273,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; dev_dbg(dev, "is_8bit=%c\n", - (host->mmc->caps | MMC_CAP_8_BIT_DATA) ? 'Y' : 'N'); + (host->mmc->caps & MMC_CAP_8_BIT_DATA) ? 'Y' : 'N'); ret = sdhci_bcm_kona_sd_reset(host); if (ret) -- cgit v0.10.2 From 14a7b416434a8076afb2dea8bb51c6514b41a47b Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 15 Sep 2015 18:32:58 +0800 Subject: mmc: sdhci: call sdhci_init() before request irq sdhci_init() will clear all irqs and set the needed irqs. So logically sdhci_init() should be called before request irq. If not, some irqs may be triggled and handled wrongly. Take the following into consideration, after request irq, if SDIO card interrupt enabled, a sd card in the sd slot will trigger a mass of interrupt(SDHCI_INT_CARD_INT), because at this time, the vmmc-regulator still not restore, no voltage supply for the sd card, so the pin of data0~data3 change and keep low, interrupt(SDHCI_INT_CARD_INT) will rise up ceaselessly. Due to we already reguest irq, system will be busy in handling this endless irq, can't response to other event. So we should call sdhci_init() before request irq in sd resume. Signed-off-by: Haibo Chen Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index fbc7efd..2cadf08 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2716,17 +2716,6 @@ int sdhci_resume_host(struct sdhci_host *host) host->ops->enable_dma(host); } - if (!device_may_wakeup(mmc_dev(host->mmc))) { - ret = request_threaded_irq(host->irq, sdhci_irq, - sdhci_thread_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); - if (ret) - return ret; - } else { - sdhci_disable_irq_wakeups(host); - disable_irq_wake(host->irq); - } - if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) && (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) { /* Card keeps power but host controller does not */ @@ -2739,6 +2728,17 @@ int sdhci_resume_host(struct sdhci_host *host) mmiowb(); } + if (!device_may_wakeup(mmc_dev(host->mmc))) { + ret = request_threaded_irq(host->irq, sdhci_irq, + sdhci_thread_irq, IRQF_SHARED, + mmc_hostname(host->mmc), host); + if (ret) + return ret; + } else { + sdhci_disable_irq_wakeups(host); + disable_irq_wake(host->irq); + } + sdhci_enable_card_detection(host); return ret; -- cgit v0.10.2 From 930d50f7fd49fe485d9126ffd26dcc0428d7920a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 16 Sep 2015 09:19:49 +0300 Subject: mmc: sdhci-of-at91: remove a line of dead code The goto is correct and the unreachable "return -EINVAL" should be removed. Signed-off-by: Dan Carpenter Acked-by: Ludovic Desroches Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index a0f05de..06d0b50 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -111,7 +111,6 @@ static int sdhci_at91_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "failed to set gck"); goto hclock_disable_unprepare; - return -EINVAL; } /* * We need to check if we have the requested rate for gck because in -- cgit v0.10.2 From bf7241d02eb4704beeaa055cb027ba7b28344a0e Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 16 Sep 2015 11:12:07 +0200 Subject: mmc: mmc_spi: Export OF module alias information Drivers needs to export the OF id table and this be built into the module or udev won't have the necessary information to autoload the driver module when the device is registered via OF. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index ae19d83..8ee11f4 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1511,6 +1511,7 @@ static const struct of_device_id mmc_spi_of_match_table[] = { { .compatible = "mmc-spi-slot", }, {}, }; +MODULE_DEVICE_TABLE(of, mmc_spi_of_match_table); static struct spi_driver mmc_spi_driver = { .driver = { -- cgit v0.10.2 From e02b9d7cbfe1536aef9e7ba7751993d7eca6e829 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 17 Sep 2015 15:09:54 +0200 Subject: mmc: sdhci-pltfm: Use of_property_read_u32 instead of open-coding it Use of_property_read_u32 instead of of_get_property with return value checks and endianness conversion. Signed-off-by: Tobias Klauser Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index a207f5a..87fb5ea 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -71,9 +71,7 @@ void sdhci_get_of_property(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - const __be32 *clk; u32 bus_width; - int size; if (of_get_property(np, "sdhci,auto-cmd12", NULL)) host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; @@ -101,9 +99,7 @@ void sdhci_get_of_property(struct platform_device *pdev) of_device_is_compatible(np, "fsl,mpc8536-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - clk = of_get_property(np, "clock-frequency", &size); - if (clk && size == sizeof(*clk) && *clk) - pltfm_host->clock = be32_to_cpup(clk); + of_property_read_u32(np, "clock-frequency", &pltfm_host->clock); if (of_find_property(np, "keep-power-in-suspend", NULL)) host->mmc->pm_caps |= MMC_PM_KEEP_POWER; -- cgit v0.10.2 From 774523539fff7bf34499b7b088dc8b2c0b4a04bc Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 17 Sep 2015 23:49:59 +0200 Subject: mmc: moxart: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 006f186..79905ce 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -711,6 +711,7 @@ static const struct of_device_id moxart_mmc_match[] = { { .compatible = "faraday,ftsdc010" }, { } }; +MODULE_DEVICE_TABLE(of, moxart_mmc_match); static struct platform_driver moxart_mmc_driver = { .probe = moxart_probe, -- cgit v0.10.2 From b73f34c22d283f049347f7f625c272ec22830eb3 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 17 Sep 2015 23:50:25 +0200 Subject: mmc: omap: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index b763b11..b9958a1 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1490,6 +1490,7 @@ static const struct of_device_id mmc_omap_match[] = { { .compatible = "ti,omap2420-mmc", }, { }, }; +MODULE_DEVICE_TABLE(of, mmc_omap_match); #endif static struct platform_driver mmc_omap_driver = { -- cgit v0.10.2 From d1f23f9304f303e15200c8b7940eeeeb1a1c8c6a Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 22 Sep 2015 09:20:08 +0800 Subject: mmc: debugfs: implement ios show for driver type This patch add ios->drv_type for mmc_ios_show to show the card's driver type. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index e914210..9adba8d 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -166,6 +166,25 @@ static int mmc_ios_show(struct seq_file *s, void *data) } seq_printf(s, "signal voltage:\t%u (%s)\n", ios->chip_select, str); + switch (ios->drv_type) { + case MMC_SET_DRIVER_TYPE_A: + str = "driver type A"; + break; + case MMC_SET_DRIVER_TYPE_B: + str = "driver type B"; + break; + case MMC_SET_DRIVER_TYPE_C: + str = "driver type C"; + break; + case MMC_SET_DRIVER_TYPE_D: + str = "driver type D"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "driver type:\t%u (%s)\n", ios->drv_type, str); + return 0; } -- cgit v0.10.2 From 8131e815f723ec8fee018d6fa34348d61c0e54b8 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 22 Sep 2015 14:00:44 +0800 Subject: mmc: mediatek: Add MMC_CAP_RUNTIME_RESUME support Add MMC_CAP_RUNTIME_RESUME support to save resume time Drop unnecessary SDC_ARG write Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 7153500..b2e89d3 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -729,7 +729,6 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO); - writel(cmd->arg, host->base + SDC_ARG); if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { @@ -1302,6 +1301,7 @@ static int msdc_drv_probe(struct platform_device *pdev) mmc->f_min = host->src_clk_freq / (4 * 255); mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps |= MMC_CAP_RUNTIME_RESUME; /* MMC core transfer sizes tunable parameters */ mmc->max_segs = MAX_BD_NUM; mmc->max_seg_size = BDMA_DESC_BUFLEN; -- cgit v0.10.2 From ce037275861ea6c016ab88a6b884adb8c481db31 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 21 Sep 2015 14:14:54 +0200 Subject: mmc: pwrseq_simple: use GPIO descriptors array API The simple power sequence provider sets a value for multiple GPIOs in one go so it is better to use the API already provided by the GPIO descriptor API instead of open coding the same logic. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 0b14b83..d10538b 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -23,18 +23,21 @@ struct mmc_pwrseq_simple { struct mmc_pwrseq pwrseq; bool clk_enabled; struct clk *ext_clk; - int nr_gpios; - struct gpio_desc *reset_gpios[0]; + struct gpio_descs *reset_gpios; }; static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, int value) { int i; + struct gpio_descs *reset_gpios = pwrseq->reset_gpios; + int values[reset_gpios->ndescs]; - for (i = 0; i < pwrseq->nr_gpios; i++) - if (!IS_ERR(pwrseq->reset_gpios[i])) - gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value); + for (i = 0; i < reset_gpios->ndescs; i++) + values[i] = value; + + gpiod_set_array_value_cansleep(reset_gpios->ndescs, reset_gpios->desc, + values); } static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) @@ -75,11 +78,8 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host) { struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, struct mmc_pwrseq_simple, pwrseq); - int i; - for (i = 0; i < pwrseq->nr_gpios; i++) - if (!IS_ERR(pwrseq->reset_gpios[i])) - gpiod_put(pwrseq->reset_gpios[i]); + gpiod_put_array(pwrseq->reset_gpios); if (!IS_ERR(pwrseq->ext_clk)) clk_put(pwrseq->ext_clk); @@ -98,14 +98,9 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) { struct mmc_pwrseq_simple *pwrseq; - int i, nr_gpios, ret = 0; - - nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios"); - if (nr_gpios < 0) - nr_gpios = 0; + int ret = 0; - pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios * - sizeof(struct gpio_desc *), GFP_KERNEL); + pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) return ERR_PTR(-ENOMEM); @@ -116,22 +111,12 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, goto free; } - for (i = 0; i < nr_gpios; i++) { - pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i, - GPIOD_OUT_HIGH); - if (IS_ERR(pwrseq->reset_gpios[i]) && - PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT && - PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) { - ret = PTR_ERR(pwrseq->reset_gpios[i]); - - while (i--) - gpiod_put(pwrseq->reset_gpios[i]); - - goto clk_put; - } + pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(pwrseq->reset_gpios)) { + ret = PTR_ERR(pwrseq->reset_gpios); + goto clk_put; } - pwrseq->nr_gpios = nr_gpios; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; return &pwrseq->pwrseq; -- cgit v0.10.2 From aaa58d0e932c518bdb1d9dc767876d7a8c68e17e Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Wed, 16 Sep 2015 14:36:10 +0800 Subject: mmc: sdhci-pltfm: enable interrupt mode to detect card for ls1021a Enable interrupt mode to detect card instead of polling mode for ls1021a by removing the quirk SDHCI_QUIRK_BROKEN_CARD_DETECTION. This could improve data transferring performance and avoid the call trace caused by polling card status sometime. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 653f335..2a54dbe 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -371,7 +371,8 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) of_device_is_compatible(np, "fsl,p5020-esdhc") || of_device_is_compatible(np, "fsl,p4080-esdhc") || of_device_is_compatible(np, "fsl,p1020-esdhc") || - of_device_is_compatible(np, "fsl,t1040-esdhc")) + of_device_is_compatible(np, "fsl,t1040-esdhc") || + of_device_is_compatible(np, "fsl,ls1021a-esdhc")) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { -- cgit v0.10.2 From a5f5774c55a2e3ed75f4d6c5556b7bfcb726e6f0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 22 Sep 2015 10:27:53 +0100 Subject: mmc: block: Add new ioctl to send multi commands Certain eMMC devices allow vendor specific device information to be read via a sequence of vendor commands. These vendor commands must be issued in sequence and an atomic fashion. One way to support this would be to add an ioctl function for sending a sequence of commands to the device atomically as proposed here. These multi commands are simple array of the existing mmc_ioc_cmd structure. The structure passed via the ioctl uses a __u64 type to specify the number of commands (so that the structure is aligned on a 64-bit boundary) and a zero length array as a header for list of commands to be issued. The maximum number of commands that can be sent is determined by MMC_IOC_MAX_CMDS (which defaults to 255 and should be more than sufficient). This based upon work by Seshagiri Holi . Signed-off-by: Seshagiri Holi Signed-off-by: Jon Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c742cfd..f6acf0f 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -387,6 +387,24 @@ out: return ERR_PTR(err); } +static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr, + struct mmc_blk_ioc_data *idata) +{ + struct mmc_ioc_cmd *ic = &idata->ic; + + if (copy_to_user(&(ic_ptr->response), ic->response, + sizeof(ic->response))) + return -EFAULT; + + if (!idata->ic.write_flag) { + if (copy_to_user((void __user *)(unsigned long)ic->data_ptr, + idata->buf, idata->buf_bytes)) + return -EFAULT; + } + + return 0; +} + static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, u32 retries_max) { @@ -447,12 +465,9 @@ out: return err; } -static int mmc_blk_ioctl_cmd(struct block_device *bdev, - struct mmc_ioc_cmd __user *ic_ptr) +static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, + struct mmc_blk_ioc_data *idata) { - struct mmc_blk_ioc_data *idata; - struct mmc_blk_data *md; - struct mmc_card *card; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct mmc_request mrq = {NULL}; @@ -461,33 +476,12 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, int is_rpmb = false; u32 status = 0; - /* - * The caller must have CAP_SYS_RAWIO, and must be calling this on the - * whole block device, not on a partition. This prevents overspray - * between sibling partitions. - */ - if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) - return -EPERM; - - idata = mmc_blk_ioctl_copy_from_user(ic_ptr); - if (IS_ERR(idata)) - return PTR_ERR(idata); - - md = mmc_blk_get(bdev->bd_disk); - if (!md) { - err = -EINVAL; - goto cmd_err; - } + if (!card || !md || !idata) + return -EINVAL; if (md->area_type & MMC_BLK_DATA_AREA_RPMB) is_rpmb = true; - card = md->queue.card; - if (IS_ERR(card)) { - err = PTR_ERR(card); - goto cmd_done; - } - cmd.opcode = idata->ic.opcode; cmd.arg = idata->ic.arg; cmd.flags = idata->ic.flags; @@ -530,23 +524,21 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mrq.cmd = &cmd; - mmc_get_card(card); - err = mmc_blk_part_switch(card, md); if (err) - goto cmd_rel_host; + return err; if (idata->ic.is_acmd) { err = mmc_app_cmd(card->host, card); if (err) - goto cmd_rel_host; + return err; } if (is_rpmb) { err = mmc_set_blockcount(card, data.blocks, idata->ic.write_flag & (1 << 31)); if (err) - goto cmd_rel_host; + return err; } if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) && @@ -557,7 +549,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, pr_err("%s: ioctl_do_sanitize() failed. err = %d", __func__, err); - goto cmd_rel_host; + return err; } mmc_wait_for_req(card->host, &mrq); @@ -565,14 +557,12 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, if (cmd.error) { dev_err(mmc_dev(card->host), "%s: cmd error %d\n", __func__, cmd.error); - err = cmd.error; - goto cmd_rel_host; + return cmd.error; } if (data.error) { dev_err(mmc_dev(card->host), "%s: data error %d\n", __func__, data.error); - err = data.error; - goto cmd_rel_host; + return data.error; } /* @@ -582,18 +572,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, if (idata->ic.postsleep_min_us) usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us); - if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) { - err = -EFAULT; - goto cmd_rel_host; - } - - if (!idata->ic.write_flag) { - if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr, - idata->buf, idata->buf_bytes)) { - err = -EFAULT; - goto cmd_rel_host; - } - } + memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp)); if (is_rpmb) { /* @@ -607,9 +586,42 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, __func__, status, err); } -cmd_rel_host: + return err; +} + +static int mmc_blk_ioctl_cmd(struct block_device *bdev, + struct mmc_ioc_cmd __user *ic_ptr) +{ + struct mmc_blk_ioc_data *idata; + struct mmc_blk_data *md; + struct mmc_card *card; + int err; + + idata = mmc_blk_ioctl_copy_from_user(ic_ptr); + if (IS_ERR(idata)) + return PTR_ERR(idata); + + md = mmc_blk_get(bdev->bd_disk); + if (!md) { + err = -EINVAL; + goto cmd_err; + } + + card = md->queue.card; + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto cmd_done; + } + + mmc_get_card(card); + + err = __mmc_blk_ioctl_cmd(card, md, idata); + mmc_put_card(card); + if (!err) + err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata); + cmd_done: mmc_blk_put(md); cmd_err: @@ -618,13 +630,97 @@ cmd_err: return err; } +static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, + struct mmc_ioc_multi_cmd __user *user) +{ + struct mmc_blk_ioc_data **idata = NULL; + struct mmc_ioc_cmd __user *cmds = user->cmds; + struct mmc_card *card; + struct mmc_blk_data *md; + int i, err = -EFAULT; + __u64 num_of_cmds; + + if (copy_from_user(&num_of_cmds, &user->num_of_cmds, + sizeof(num_of_cmds))) + return -EFAULT; + + if (num_of_cmds > MMC_IOC_MAX_CMDS) + return -EINVAL; + + idata = kcalloc(num_of_cmds, sizeof(*idata), GFP_KERNEL); + if (!idata) + return -ENOMEM; + + for (i = 0; i < num_of_cmds; i++) { + idata[i] = mmc_blk_ioctl_copy_from_user(&cmds[i]); + if (IS_ERR(idata[i])) { + err = PTR_ERR(idata[i]); + num_of_cmds = i; + goto cmd_err; + } + } + + md = mmc_blk_get(bdev->bd_disk); + if (!md) + goto cmd_err; + + card = md->queue.card; + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto cmd_done; + } + + mmc_get_card(card); + + for (i = 0; i < num_of_cmds; i++) { + err = __mmc_blk_ioctl_cmd(card, md, idata[i]); + if (err) { + mmc_put_card(card); + goto cmd_done; + } + } + + mmc_put_card(card); + + /* copy to user if data and response */ + for (i = 0; i < num_of_cmds; i++) { + err = mmc_blk_ioctl_copy_to_user(&cmds[i], idata[i]); + if (err) + break; + } + +cmd_done: + mmc_blk_put(md); +cmd_err: + for (i = 0; i < num_of_cmds; i++) { + kfree(idata[i]->buf); + kfree(idata[i]); + } + kfree(idata); + return err; +} + static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - int ret = -EINVAL; - if (cmd == MMC_IOC_CMD) - ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg); - return ret; + /* + * The caller must have CAP_SYS_RAWIO, and must be calling this on the + * whole block device, not on a partition. This prevents overspray + * between sibling partitions. + */ + if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) + return -EPERM; + + switch (cmd) { + case MMC_IOC_CMD: + return mmc_blk_ioctl_cmd(bdev, + (struct mmc_ioc_cmd __user *)arg); + case MMC_IOC_MULTI_CMD: + return mmc_blk_ioctl_multi_cmd(bdev, + (struct mmc_ioc_multi_cmd __user *)arg); + default: + return -EINVAL; + } } #ifdef CONFIG_COMPAT diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h index 1f5e689..7e385b8 100644 --- a/include/uapi/linux/mmc/ioctl.h +++ b/include/uapi/linux/mmc/ioctl.h @@ -45,8 +45,24 @@ struct mmc_ioc_cmd { }; #define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long) ptr -#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) +/** + * struct mmc_ioc_multi_cmd - multi command information + * @num_of_cmds: Number of commands to send. Must be equal to or less than + * MMC_IOC_MAX_CMDS. + * @cmds: Array of commands with length equal to 'num_of_cmds' + */ +struct mmc_ioc_multi_cmd { + __u64 num_of_cmds; + struct mmc_ioc_cmd cmds[0]; +}; +#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) +/* + * MMC_IOC_MULTI_CMD: Used to send an array of MMC commands described by + * the structure mmc_ioc_multi_cmd. The MMC driver will issue all + * commands in array in sequence to card. + */ +#define MMC_IOC_MULTI_CMD _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_multi_cmd) /* * Since this ioctl is only meant to enhance (and not replace) normal access * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES @@ -54,4 +70,5 @@ struct mmc_ioc_cmd { * block device operations. */ #define MMC_IOC_MAX_BYTES (512L * 256) +#define MMC_IOC_MAX_CMDS 255 #endif /* LINUX_MMC_IOCTL_H */ -- cgit v0.10.2 From de98c55682790437fb3c6b5335f2bf3ce20539db Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 Sep 2015 17:30:24 +0200 Subject: mmc: Add mmc_is_io_op helper function Add a helper function to check if an opcode is a sd-io-rw-* opcode. Signed-off-by: Hans de Goede Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h index 12a4d3a..5660c7f 100644 --- a/drivers/mmc/core/sdio_ops.h +++ b/drivers/mmc/core/sdio_ops.h @@ -12,6 +12,8 @@ #ifndef _MMC_SDIO_OPS_H #define _MMC_SDIO_OPS_H +#include + int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8* out); @@ -19,5 +21,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); int sdio_reset(struct mmc_host *host); +static inline bool mmc_is_io_op(u32 opcode) +{ + return opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED; +} + #endif -- cgit v0.10.2 From 5d3f6ef04d65dda92a3573841a7215fb05c347de Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 Sep 2015 17:30:25 +0200 Subject: mmc: Wait for card_busy before starting sdio requests Some sdio wifi chips will not work properly if we try to start new sdio-rw requests while the device is signalling that it is busy. Signed-off-by: Hans de Goede Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2092a89..21cda23 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -206,6 +206,23 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) return; } + /* + * For sdio rw commands we must wait for card busy otherwise some + * sdio devices won't work properly. + */ + if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) { + int tries = 500; /* Wait aprox 500ms at maximum */ + + while (host->ops->card_busy(host) && --tries) + mmc_delay(1); + + if (tries == 0) { + mrq->cmd->error = -EBUSY; + mmc_request_done(host, mrq); + return; + } + } + host->ops->request(host, mrq); } -- cgit v0.10.2 From c1590dd86d6e34df214f30a501682e392be0ba3c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 Sep 2015 17:30:26 +0200 Subject: mmc: sunxi: Add card busy detection Some sdio wifi modules have not been working reliable with the sunxi-mmc host code. This turns out to be caused by starting new io-rw commands while the card signals that it is still busy processing a previous command. This commit adds card-busy detection to the sunxi-mmc driver which together with recent core changes to check card-busy before starting io-rw commands fixes the wifi reliability issues on the Cubietruck and other sunxi boards using sdio wifi. Reported-by: Eugene K Suggested-by: Eugene K Cc: Eugene K Cc: Arend van Spriel Signed-off-by: Hans de Goede Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index b981b85..83de82b 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -873,6 +873,13 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, iflags); } +static int sunxi_mmc_card_busy(struct mmc_host *mmc) +{ + struct sunxi_mmc_host *host = mmc_priv(mmc); + + return !!(mmc_readl(host, REG_STAS) & SDXC_CARD_DATA_BUSY); +} + static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun4i-a10-mmc", }, { .compatible = "allwinner,sun5i-a13-mmc", }, @@ -888,6 +895,7 @@ static struct mmc_host_ops sunxi_mmc_ops = { .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = sunxi_mmc_enable_sdio_irq, .hw_reset = sunxi_mmc_hw_reset, + .card_busy = sunxi_mmc_card_busy, }; static const struct sunxi_mmc_clk_delay sunxi_mmc_clk_delays[] = { -- cgit v0.10.2 From 85f45058eeef94fdc9f533cca8e17679e44f1177 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 24 Sep 2015 08:13:43 +0800 Subject: mmc: debugfs: implement ios show for SDR12 and SDR25 This patch add MMC_TIMING_UHS_SDR12 and MMC_TIMING_UHS_SDR25 for mmc_ios_show to show the ios->timing if mmc card runs under these two modes. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 9adba8d..f4db93e 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -126,6 +126,12 @@ static int mmc_ios_show(struct seq_file *s, void *data) case MMC_TIMING_SD_HS: str = "sd high-speed"; break; + case MMC_TIMING_UHS_SDR12: + str = "sd uhs SDR12"; + break; + case MMC_TIMING_UHS_SDR25: + str = "sd uhs SDR25"; + break; case MMC_TIMING_UHS_SDR50: str = "sd uhs SDR50"; break; -- cgit v0.10.2 From b093410c9aef29efbf2f4af7b00b2dcc1c119fe8 Mon Sep 17 00:00:00 2001 From: Grant Grundler Date: Wed, 23 Sep 2015 18:30:33 -0700 Subject: mmc: block: copy resp[] data on err for MMC_IOC_MULTI_CMD MMC_IOC_CMD and MMC_IOC_MULTI_CMD ioctl() code currently bails on any eMMC errors. However, in case there is any resp[] data, we should attempt to copy resp[] back to user space. The user app can then determine which command(s) failed in the MMC_IOC_MULTI_CMD case AND/OR report better diagnostics in both cases. Gwendal Grignou provided the idea and it was previously implemented and tested on v3.18 ChromeOS kernel: https://chromium-review.googlesource.com/#/c/299956 Signed-off-by: Grant Grundler Reviewed-by: Hyung Taek Ryoo Reviewed-by: Gwendal Grignou Tested-by: David Riley Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index f6acf0f..23b6c8e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -595,7 +595,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, struct mmc_blk_ioc_data *idata; struct mmc_blk_data *md; struct mmc_card *card; - int err; + int err = 0, ioc_err = 0; idata = mmc_blk_ioctl_copy_from_user(ic_ptr); if (IS_ERR(idata)) @@ -615,19 +615,18 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mmc_get_card(card); - err = __mmc_blk_ioctl_cmd(card, md, idata); + ioc_err = __mmc_blk_ioctl_cmd(card, md, idata); mmc_put_card(card); - if (!err) - err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata); + err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata); cmd_done: mmc_blk_put(md); cmd_err: kfree(idata->buf); kfree(idata); - return err; + return ioc_err ? ioc_err : err; } static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, @@ -637,7 +636,7 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, struct mmc_ioc_cmd __user *cmds = user->cmds; struct mmc_card *card; struct mmc_blk_data *md; - int i, err = -EFAULT; + int i, err = 0, ioc_err = 0; __u64 num_of_cmds; if (copy_from_user(&num_of_cmds, &user->num_of_cmds, @@ -672,22 +671,14 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, mmc_get_card(card); - for (i = 0; i < num_of_cmds; i++) { - err = __mmc_blk_ioctl_cmd(card, md, idata[i]); - if (err) { - mmc_put_card(card); - goto cmd_done; - } - } + for (i = 0; i < num_of_cmds && !ioc_err; i++) + ioc_err = __mmc_blk_ioctl_cmd(card, md, idata[i]); mmc_put_card(card); /* copy to user if data and response */ - for (i = 0; i < num_of_cmds; i++) { + for (i = 0; i < num_of_cmds && !err; i++) err = mmc_blk_ioctl_copy_to_user(&cmds[i], idata[i]); - if (err) - break; - } cmd_done: mmc_blk_put(md); @@ -697,7 +688,7 @@ cmd_err: kfree(idata[i]); } kfree(idata); - return err; + return ioc_err ? ioc_err : err; } static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, -- cgit v0.10.2 From 4324f6de6d2eb9b232410eb0d67bfafdde8ba711 Mon Sep 17 00:00:00 2001 From: Weijun Yang Date: Sun, 4 Oct 2015 12:04:11 +0000 Subject: mmc: core: enable CMD19 tuning for DDR50 mode As SD Specifications Part1 Physical Layer Specification Version 3.01 says, CMD19 tuning is available for unlocked cards in transfer state of 1.8V signaling mode. The small difference between v3.00 and 3.01 spec means that CMD19 tuning is also available for DDR50 mode. Signed-off-by: Weijun Yang Signed-off-by: Barry Song Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index e28ebf3..e124db0 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -626,9 +626,25 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. */ if (!mmc_host_is_spi(card->host) && - (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || - card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) + (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || + card->sd_bus_speed == UHS_DDR50_BUS_SPEED || + card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) { err = mmc_execute_tuning(card); + + /* + * As SD Specifications Part1 Physical Layer Specification + * Version 3.01 says, CMD19 tuning is available for unlocked + * cards in transfer state of 1.8V signaling mode. The small + * difference between v3.00 and 3.01 spec means that CMD19 + * tuning is also available for DDR50 mode. + */ + if (err && card->sd_bus_speed == UHS_DDR50_BUS_SPEED) { + pr_warn("%s: ddr50 tuning failed\n", + mmc_hostname(card->host)); + err = 0; + } + } + out: kfree(status); -- cgit v0.10.2 From 9faac7b95ea4f9e83b7a914084cc81ef1632fd91 Mon Sep 17 00:00:00 2001 From: Weijun Yang Date: Sun, 4 Oct 2015 12:04:12 +0000 Subject: mmc: sdhci: enable tuning for DDR50 CMD19 tuning is also available for DDR50 mode. Signed-off-by: Weijun Yang Signed-off-by: Barry Song Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2cadf08..b48565e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1895,9 +1895,9 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) tuning_count = host->tuning_count; /* - * The Host Controller needs tuning only in case of SDR104 mode - * and for SDR50 mode when Use Tuning for SDR50 is set in the - * Capabilities register. + * The Host Controller needs tuning in case of SDR104 and DDR50 + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in + * the Capabilities register. * If the Host Controller supports the HS200 mode then the * tuning function has to be executed. */ @@ -1917,6 +1917,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) break; case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: break; case MMC_TIMING_UHS_SDR50: -- cgit v0.10.2 From 0de9125fa823e526c9f68dc0987aae342ec94cbd Mon Sep 17 00:00:00 2001 From: Weijun Yang Date: Sun, 4 Oct 2015 12:04:13 +0000 Subject: mmc: sirf: fix the capbility to support DDR50 According to hardware spec, validate DDR50 mode for SDXC. Signed-off-by: Weijun Yang Signed-off-by: Barry Song Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 8842945..f5488c4 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -50,7 +50,8 @@ static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg) if (unlikely((reg == SDHCI_CAPABILITIES_1) && (host->mmc->caps & MMC_CAP_UHS_SDR50))) { /* fake CAP_1 register */ - val = SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; + val = SDHCI_SUPPORT_DDR50 | + SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; } if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) { -- cgit v0.10.2 From 659c9bc114a810b3a3c1e50585cc57f1312a6d60 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 5 Oct 2015 20:31:18 +0100 Subject: mmc: sdhci-pci: Build o2micro support in the same module sdhci-pci-o2micro.c contains no initialisation and its functions are only called from shdci-pci.c, so there is no reason for it to be a separate module, let alone or for it to always be built-in. - Rename sdhci-pci.c to sdhci-pci-core.c so that the sdhci-pci module can be built from multiple source files - Add sdhci-pci-o2micro.c to it - Remove redundant exports Signed-off-by: Ben Hutchings Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 4f3452a..3595f83 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -9,8 +9,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o +sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o -obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-o2micro.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c new file mode 100644 index 0000000..b3b0a3e --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -0,0 +1,1816 @@ +/* linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface + * + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * Thanks to the following companies for their support: + * + * - JMicron (hardware and technical support) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci.h" +#include "sdhci-pci.h" +#include "sdhci-pci-o2micro.h" + +/*****************************************************************************\ + * * + * Hardware specific quirk handling * + * * +\*****************************************************************************/ + +static int ricoh_probe(struct sdhci_pci_chip *chip) +{ + if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || + chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) + chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; + return 0; +} + +static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->caps = + ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) + & SDHCI_TIMEOUT_CLK_MASK) | + + ((0x21 << SDHCI_CLOCK_BASE_SHIFT) + & SDHCI_CLOCK_BASE_MASK) | + + SDHCI_TIMEOUT_CLK_UNIT | + SDHCI_CAN_VDD_330 | + SDHCI_CAN_DO_HISPD | + SDHCI_CAN_DO_SDMA; + return 0; +} + +static int ricoh_mmc_resume(struct sdhci_pci_chip *chip) +{ + /* Apply a delay to allow controller to settle */ + /* Otherwise it becomes confused if card state changed + during suspend */ + msleep(500); + return 0; +} + +static const struct sdhci_pci_fixes sdhci_ricoh = { + .probe = ricoh_probe, + .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_FORCE_DMA | + SDHCI_QUIRK_CLOCK_BEFORE_RESET, +}; + +static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { + .probe_slot = ricoh_mmc_probe_slot, + .resume = ricoh_mmc_resume, + .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_CLOCK_BEFORE_RESET | + SDHCI_QUIRK_NO_CARD_NO_RESET | + SDHCI_QUIRK_MISSING_CAPS +}; + +static const struct sdhci_pci_fixes sdhci_ene_712 = { + .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | + SDHCI_QUIRK_BROKEN_DMA, +}; + +static const struct sdhci_pci_fixes sdhci_ene_714 = { + .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | + SDHCI_QUIRK_BROKEN_DMA, +}; + +static const struct sdhci_pci_fixes sdhci_cafe = { + .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | + SDHCI_QUIRK_NO_BUSY_IRQ | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, +}; + +static const struct sdhci_pci_fixes sdhci_intel_qrk = { + .quirks = SDHCI_QUIRK_NO_HISPD_BIT, +}; + +static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; + return 0; +} + +/* + * ADMA operation is disabled for Moorestown platform due to + * hardware bugs. + */ +static int mrst_hc_probe(struct sdhci_pci_chip *chip) +{ + /* + * slots number is fixed here for MRST as SDIO3/5 are never used and + * have hardware bugs. + */ + chip->num_slots = 1; + return 0; +} + +static int pch_hc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; + return 0; +} + +#ifdef CONFIG_PM + +static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id) +{ + struct sdhci_pci_slot *slot = dev_id; + struct sdhci_host *host = slot->host; + + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + return IRQ_HANDLED; +} + +static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) +{ + int err, irq, gpio = slot->cd_gpio; + + slot->cd_gpio = -EINVAL; + slot->cd_irq = -EINVAL; + + if (!gpio_is_valid(gpio)) + return; + + err = gpio_request(gpio, "sd_cd"); + if (err < 0) + goto out; + + err = gpio_direction_input(gpio); + if (err < 0) + goto out_free; + + irq = gpio_to_irq(gpio); + if (irq < 0) + goto out_free; + + err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, "sd_cd", slot); + if (err) + goto out_free; + + slot->cd_gpio = gpio; + slot->cd_irq = irq; + + return; + +out_free: + gpio_free(gpio); +out: + dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n"); +} + +static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) +{ + if (slot->cd_irq >= 0) + free_irq(slot->cd_irq, slot); + if (gpio_is_valid(slot->cd_gpio)) + gpio_free(slot->cd_gpio); +} + +#else + +static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) +{ +} + +static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) +{ +} + +#endif + +static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; + slot->host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC | + MMC_CAP2_HC_ERASE_SZ; + return 0; +} + +static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE; + return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { + .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, + .probe_slot = mrst_hc_probe_slot, +}; + +static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = { + .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, + .probe = mrst_hc_probe, +}; + +static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .allow_runtime_pm = true, + .own_cd_for_runtime_pm = true, +}; + +static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, + .allow_runtime_pm = true, + .probe_slot = mfd_sdio_probe_slot, +}; + +static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .allow_runtime_pm = true, + .probe_slot = mfd_emmc_probe_slot, +}; + +static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { + .quirks = SDHCI_QUIRK_BROKEN_ADMA, + .probe_slot = pch_hc_probe_slot, +}; + +static void sdhci_pci_int_hw_reset(struct sdhci_host *host) +{ + u8 reg; + + reg = sdhci_readb(host, SDHCI_POWER_CONTROL); + reg |= 0x10; + sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); + /* For eMMC, minimum is 1us but give it 9us for good measure */ + udelay(9); + reg &= ~0x10; + sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + usleep_range(300, 1000); +} + +static int spt_select_drive_strength(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, + int host_drv, int card_drv, int *drv_type) +{ + int drive_strength; + + if (sdhci_pci_spt_drive_strength > 0) + drive_strength = sdhci_pci_spt_drive_strength & 0xf; + else + drive_strength = 1; /* 33-ohm */ + + if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0) + drive_strength = 0; /* Default 50-ohm */ + + return drive_strength; +} + +/* Try to read the drive strength from the card */ +static void spt_read_drive_strength(struct sdhci_host *host) +{ + u32 val, i, t; + u16 m; + + if (sdhci_pci_spt_drive_strength) + return; + + sdhci_pci_spt_drive_strength = -1; + + m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7; + if (m != 3 && m != 5) + return; + val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (val & 0x3) + return; + sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE); + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + sdhci_writew(host, 512, SDHCI_BLOCK_SIZE); + sdhci_writew(host, 1, SDHCI_BLOCK_COUNT); + sdhci_writel(host, 0, SDHCI_ARGUMENT); + sdhci_writew(host, 0x83b, SDHCI_COMMAND); + for (i = 0; i < 1000; i++) { + val = sdhci_readl(host, SDHCI_INT_STATUS); + if (val & 0xffff8000) + return; + if (val & 0x20) + break; + udelay(1); + } + val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (!(val & 0x800)) + return; + for (i = 0; i < 47; i++) + val = sdhci_readl(host, SDHCI_BUFFER); + t = val & 0xf00; + if (t != 0x200 && t != 0x300) + return; + + sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf); +} + +static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | + MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; + slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; + slot->hw_reset = sdhci_pci_int_hw_reset; + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) + slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) { + spt_read_drive_strength(slot->host); + slot->select_drive_strength = spt_select_drive_strength; + } + return 0; +} + +static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE | + MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; + return 0; +} + +static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_WAIT_WHILE_BUSY; + slot->cd_con_id = NULL; + slot->cd_idx = 0; + slot->cd_override_level = true; + return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { + .allow_runtime_pm = true, + .probe_slot = byt_emmc_probe_slot, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | + SDHCI_QUIRK2_STOP_WITH_TC, +}; + +static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .allow_runtime_pm = true, + .probe_slot = byt_sdio_probe_slot, +}; + +static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, + .allow_runtime_pm = true, + .own_cd_for_runtime_pm = true, + .probe_slot = byt_sd_probe_slot, +}; + +/* Define Host controllers for Intel Merrifield platform */ +#define INTEL_MRFL_EMMC_0 0 +#define INTEL_MRFL_EMMC_1 1 + +static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ + if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) && + (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1)) + /* SD support is not ready yet */ + return -ENODEV; + + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_1_8V_DDR; + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .allow_runtime_pm = true, + .probe_slot = intel_mrfl_mmc_probe_slot, +}; + +/* O2Micro extra registers */ +#define O2_SD_LOCK_WP 0xD3 +#define O2_SD_MULTI_VCC3V 0xEE +#define O2_SD_CLKREQ 0xEC +#define O2_SD_CAPS 0xE0 +#define O2_SD_ADMA1 0xE2 +#define O2_SD_ADMA2 0xE7 +#define O2_SD_INF_MOD 0xF1 + +static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) +{ + u8 scratch; + int ret; + + ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch); + if (ret) + return ret; + + /* + * Turn PMOS on [bit 0], set over current detection to 2.4 V + * [bit 1:2] and enable over current debouncing [bit 6]. + */ + if (on) + scratch |= 0x47; + else + scratch &= ~0x47; + + ret = pci_write_config_byte(chip->pdev, 0xAE, scratch); + if (ret) + return ret; + + return 0; +} + +static int jmicron_probe(struct sdhci_pci_chip *chip) +{ + int ret; + u16 mmcdev = 0; + + if (chip->pdev->revision == 0) { + chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_32BIT_DMA_SIZE | + SDHCI_QUIRK_32BIT_ADMA_SIZE | + SDHCI_QUIRK_RESET_AFTER_REQUEST | + SDHCI_QUIRK_BROKEN_SMALL_PIO; + } + + /* + * JMicron chips can have two interfaces to the same hardware + * in order to work around limitations in Microsoft's driver. + * We need to make sure we only bind to one of them. + * + * This code assumes two things: + * + * 1. The PCI code adds subfunctions in order. + * + * 2. The MMC interface has a lower subfunction number + * than the SD interface. + */ + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) + mmcdev = PCI_DEVICE_ID_JMICRON_JMB38X_MMC; + else if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD) + mmcdev = PCI_DEVICE_ID_JMICRON_JMB388_ESD; + + if (mmcdev) { + struct pci_dev *sd_dev; + + sd_dev = NULL; + while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, + mmcdev, sd_dev)) != NULL) { + if ((PCI_SLOT(chip->pdev->devfn) == + PCI_SLOT(sd_dev->devfn)) && + (chip->pdev->bus == sd_dev->bus)) + break; + } + + if (sd_dev) { + pci_dev_put(sd_dev); + dev_info(&chip->pdev->dev, "Refusing to bind to " + "secondary interface.\n"); + return -ENODEV; + } + } + + /* + * JMicron chips need a bit of a nudge to enable the power + * output pins. + */ + ret = jmicron_pmos(chip, 1); + if (ret) { + dev_err(&chip->pdev->dev, "Failure enabling card power\n"); + return ret; + } + + /* quirk for unsable RO-detection on JM388 chips */ + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD || + chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) + chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT; + + return 0; +} + +static void jmicron_enable_mmc(struct sdhci_host *host, int on) +{ + u8 scratch; + + scratch = readb(host->ioaddr + 0xC0); + + if (on) + scratch |= 0x01; + else + scratch &= ~0x01; + + writeb(scratch, host->ioaddr + 0xC0); +} + +static int jmicron_probe_slot(struct sdhci_pci_slot *slot) +{ + if (slot->chip->pdev->revision == 0) { + u16 version; + + version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION); + version = (version & SDHCI_VENDOR_VER_MASK) >> + SDHCI_VENDOR_VER_SHIFT; + + /* + * Older versions of the chip have lots of nasty glitches + * in the ADMA engine. It's best just to avoid it + * completely. + */ + if (version < 0xAC) + slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; + } + + /* JM388 MMC doesn't support 1.8V while SD supports it */ + if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { + slot->host->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34 | + MMC_VDD_29_30 | MMC_VDD_30_31 | + MMC_VDD_165_195; /* allow 1.8V */ + slot->host->ocr_avail_mmc = MMC_VDD_32_33 | MMC_VDD_33_34 | + MMC_VDD_29_30 | MMC_VDD_30_31; /* no 1.8V for MMC */ + } + + /* + * The secondary interface requires a bit set to get the + * interrupts. + */ + if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || + slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) + jmicron_enable_mmc(slot->host, 1); + + slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST; + + return 0; +} + +static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) +{ + if (dead) + return; + + if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || + slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) + jmicron_enable_mmc(slot->host, 0); +} + +static int jmicron_suspend(struct sdhci_pci_chip *chip) +{ + int i; + + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || + chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { + for (i = 0; i < chip->num_slots; i++) + jmicron_enable_mmc(chip->slots[i]->host, 0); + } + + return 0; +} + +static int jmicron_resume(struct sdhci_pci_chip *chip) +{ + int ret, i; + + if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || + chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { + for (i = 0; i < chip->num_slots; i++) + jmicron_enable_mmc(chip->slots[i]->host, 1); + } + + ret = jmicron_pmos(chip, 1); + if (ret) { + dev_err(&chip->pdev->dev, "Failure enabling card power\n"); + return ret; + } + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_o2 = { + .probe = sdhci_pci_o2_probe, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, + .probe_slot = sdhci_pci_o2_probe_slot, + .resume = sdhci_pci_o2_resume, +}; + +static const struct sdhci_pci_fixes sdhci_jmicron = { + .probe = jmicron_probe, + + .probe_slot = jmicron_probe_slot, + .remove_slot = jmicron_remove_slot, + + .suspend = jmicron_suspend, + .resume = jmicron_resume, +}; + +/* SysKonnect CardBus2SDIO extra registers */ +#define SYSKT_CTRL 0x200 +#define SYSKT_RDFIFO_STAT 0x204 +#define SYSKT_WRFIFO_STAT 0x208 +#define SYSKT_POWER_DATA 0x20c +#define SYSKT_POWER_330 0xef +#define SYSKT_POWER_300 0xf8 +#define SYSKT_POWER_184 0xcc +#define SYSKT_POWER_CMD 0x20d +#define SYSKT_POWER_START (1 << 7) +#define SYSKT_POWER_STATUS 0x20e +#define SYSKT_POWER_STATUS_OK (1 << 0) +#define SYSKT_BOARD_REV 0x210 +#define SYSKT_CHIP_REV 0x211 +#define SYSKT_CONF_DATA 0x212 +#define SYSKT_CONF_DATA_1V8 (1 << 2) +#define SYSKT_CONF_DATA_2V5 (1 << 1) +#define SYSKT_CONF_DATA_3V3 (1 << 0) + +static int syskt_probe(struct sdhci_pci_chip *chip) +{ + if ((chip->pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { + chip->pdev->class &= ~0x0000FF; + chip->pdev->class |= PCI_SDHCI_IFDMA; + } + return 0; +} + +static int syskt_probe_slot(struct sdhci_pci_slot *slot) +{ + int tm, ps; + + u8 board_rev = readb(slot->host->ioaddr + SYSKT_BOARD_REV); + u8 chip_rev = readb(slot->host->ioaddr + SYSKT_CHIP_REV); + dev_info(&slot->chip->pdev->dev, "SysKonnect CardBus2SDIO, " + "board rev %d.%d, chip rev %d.%d\n", + board_rev >> 4, board_rev & 0xf, + chip_rev >> 4, chip_rev & 0xf); + if (chip_rev >= 0x20) + slot->host->quirks |= SDHCI_QUIRK_FORCE_DMA; + + writeb(SYSKT_POWER_330, slot->host->ioaddr + SYSKT_POWER_DATA); + writeb(SYSKT_POWER_START, slot->host->ioaddr + SYSKT_POWER_CMD); + udelay(50); + tm = 10; /* Wait max 1 ms */ + do { + ps = readw(slot->host->ioaddr + SYSKT_POWER_STATUS); + if (ps & SYSKT_POWER_STATUS_OK) + break; + udelay(100); + } while (--tm); + if (!tm) { + dev_err(&slot->chip->pdev->dev, + "power regulator never stabilized"); + writeb(0, slot->host->ioaddr + SYSKT_POWER_CMD); + return -ENODEV; + } + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_syskt = { + .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER, + .probe = syskt_probe, + .probe_slot = syskt_probe_slot, +}; + +static int via_probe(struct sdhci_pci_chip *chip) +{ + if (chip->pdev->revision == 0x10) + chip->quirks |= SDHCI_QUIRK_DELAY_AFTER_POWER; + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_via = { + .probe = via_probe, +}; + +static int rtsx_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps2 |= MMC_CAP2_HS200; + return 0; +} + +static const struct sdhci_pci_fixes sdhci_rtsx = { + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_BROKEN_64_BIT_DMA | + SDHCI_QUIRK2_BROKEN_DDR50, + .probe_slot = rtsx_probe_slot, +}; + +/*AMD chipset generation*/ +enum amd_chipset_gen { + AMD_CHIPSET_BEFORE_ML, + AMD_CHIPSET_CZ, + AMD_CHIPSET_NL, + AMD_CHIPSET_UNKNOWN, +}; + +static int amd_probe(struct sdhci_pci_chip *chip) +{ + struct pci_dev *smbus_dev; + enum amd_chipset_gen gen; + + smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); + if (smbus_dev) { + gen = AMD_CHIPSET_BEFORE_ML; + } else { + smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL); + if (smbus_dev) { + if (smbus_dev->revision < 0x51) + gen = AMD_CHIPSET_CZ; + else + gen = AMD_CHIPSET_NL; + } else { + gen = AMD_CHIPSET_UNKNOWN; + } + } + + if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) { + chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; + chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; + } + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_amd = { + .probe = amd_probe, +}; + +static const struct pci_device_id pci_ids[] = { + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = PCI_DEVICE_ID_RICOH_R5C822, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh, + }, + + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0x843, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0xe822, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0xe823, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB712_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ene_712, + }, + + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB712_SD_2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ene_712, + }, + + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB714_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ene_714, + }, + + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB714_SD_2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ene_714, + }, + + { + .vendor = PCI_VENDOR_ID_MARVELL, + .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_cafe, + }, + + { + .vendor = PCI_VENDOR_ID_JMICRON, + .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_jmicron, + }, + + { + .vendor = PCI_VENDOR_ID_JMICRON, + .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_jmicron, + }, + + { + .vendor = PCI_VENDOR_ID_JMICRON, + .device = PCI_DEVICE_ID_JMICRON_JMB388_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_jmicron, + }, + + { + .vendor = PCI_VENDOR_ID_JMICRON, + .device = PCI_DEVICE_ID_JMICRON_JMB388_ESD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_jmicron, + }, + + { + .vendor = PCI_VENDOR_ID_SYSKONNECT, + .device = 0x8000, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_syskt, + }, + + { + .vendor = PCI_VENDOR_ID_VIA, + .device = 0x95d0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_via, + }, + + { + .vendor = PCI_VENDOR_ID_REALTEK, + .device = 0x5250, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_rtsx, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_QRK_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_qrk, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRST_SD0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc0, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRST_SD1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1_hc2, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRST_SD2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1_hc2, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_SDIO2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_EMMC0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_EMMC1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PCH_SDIO0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PCH_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_EMMC2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BSW_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BSW_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BSW_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_EMMC0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_EMMC1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRFL_MMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_SPT_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_SPT_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_SPT_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_8120, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_8220, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_8221, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_8320, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_8321, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_FUJIN2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SDS0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SDS1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SEABIRD0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SEABIRD1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + { + .vendor = PCI_VENDOR_ID_AMD, + .device = PCI_ANY_ID, + .class = PCI_CLASS_SYSTEM_SDHCI << 8, + .class_mask = 0xFFFF00, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_amd, + }, + { /* Generic SD host controller */ + PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) + }, + + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(pci, pci_ids); + +/*****************************************************************************\ + * * + * SDHCI core callbacks * + * * +\*****************************************************************************/ + +static int sdhci_pci_enable_dma(struct sdhci_host *host) +{ + struct sdhci_pci_slot *slot; + struct pci_dev *pdev; + int ret = -1; + + slot = sdhci_priv(host); + pdev = slot->chip->pdev; + + if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && + ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && + (host->flags & SDHCI_USE_SDMA)) { + dev_warn(&pdev->dev, "Will use DMA mode even though HW " + "doesn't fully claim to support it.\n"); + } + + if (host->flags & SDHCI_USE_64_BIT_DMA) { + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) { + host->flags &= ~SDHCI_USE_64_BIT_DMA; + } else { + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (ret) + dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n"); + } + } + if (ret) + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + pci_set_master(pdev); + + return 0; +} + +static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width) +{ + u8 ctrl; + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + + switch (width) { + case MMC_BUS_WIDTH_8: + ctrl |= SDHCI_CTRL_8BITBUS; + ctrl &= ~SDHCI_CTRL_4BITBUS; + break; + case MMC_BUS_WIDTH_4: + ctrl |= SDHCI_CTRL_4BITBUS; + ctrl &= ~SDHCI_CTRL_8BITBUS; + break; + default: + ctrl &= ~(SDHCI_CTRL_8BITBUS | SDHCI_CTRL_4BITBUS); + break; + } + + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} + +static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + int rst_n_gpio = slot->rst_n_gpio; + + if (!gpio_is_valid(rst_n_gpio)) + return; + gpio_set_value_cansleep(rst_n_gpio, 0); + /* For eMMC, minimum is 1us but give it 10us for good measure */ + udelay(10); + gpio_set_value_cansleep(rst_n_gpio, 1); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + usleep_range(300, 1000); +} + +static void sdhci_pci_hw_reset(struct sdhci_host *host) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + + if (slot->hw_reset) + slot->hw_reset(host); +} + +static int sdhci_pci_select_drive_strength(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + + if (!slot->select_drive_strength) + return 0; + + return slot->select_drive_strength(host, card, max_dtr, host_drv, + card_drv, drv_type); +} + +static const struct sdhci_ops sdhci_pci_ops = { + .set_clock = sdhci_set_clock, + .enable_dma = sdhci_pci_enable_dma, + .set_bus_width = sdhci_pci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .hw_reset = sdhci_pci_hw_reset, + .select_drive_strength = sdhci_pci_select_drive_strength, +}; + +/*****************************************************************************\ + * * + * Suspend/resume * + * * +\*****************************************************************************/ + +#ifdef CONFIG_PM + +static int sdhci_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot; + mmc_pm_flag_t slot_pm_flags; + mmc_pm_flag_t pm_flags = 0; + int i, ret; + + chip = pci_get_drvdata(pdev); + if (!chip) + return 0; + + for (i = 0; i < chip->num_slots; i++) { + slot = chip->slots[i]; + if (!slot) + continue; + + ret = sdhci_suspend_host(slot->host); + + if (ret) + goto err_pci_suspend; + + slot_pm_flags = slot->host->mmc->pm_flags; + if (slot_pm_flags & MMC_PM_WAKE_SDIO_IRQ) + sdhci_enable_irq_wakeups(slot->host); + + pm_flags |= slot_pm_flags; + } + + if (chip->fixes && chip->fixes->suspend) { + ret = chip->fixes->suspend(chip); + if (ret) + goto err_pci_suspend; + } + + if (pm_flags & MMC_PM_KEEP_POWER) { + if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) + device_init_wakeup(dev, true); + else + device_init_wakeup(dev, false); + } else + device_init_wakeup(dev, false); + + return 0; + +err_pci_suspend: + while (--i >= 0) + sdhci_resume_host(chip->slots[i]->host); + return ret; +} + +static int sdhci_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot; + int i, ret; + + chip = pci_get_drvdata(pdev); + if (!chip) + return 0; + + if (chip->fixes && chip->fixes->resume) { + ret = chip->fixes->resume(chip); + if (ret) + return ret; + } + + for (i = 0; i < chip->num_slots; i++) { + slot = chip->slots[i]; + if (!slot) + continue; + + ret = sdhci_resume_host(slot->host); + if (ret) + return ret; + } + + return 0; +} + +static int sdhci_pci_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot; + int i, ret; + + chip = pci_get_drvdata(pdev); + if (!chip) + return 0; + + for (i = 0; i < chip->num_slots; i++) { + slot = chip->slots[i]; + if (!slot) + continue; + + ret = sdhci_runtime_suspend_host(slot->host); + + if (ret) + goto err_pci_runtime_suspend; + } + + if (chip->fixes && chip->fixes->suspend) { + ret = chip->fixes->suspend(chip); + if (ret) + goto err_pci_runtime_suspend; + } + + return 0; + +err_pci_runtime_suspend: + while (--i >= 0) + sdhci_runtime_resume_host(chip->slots[i]->host); + return ret; +} + +static int sdhci_pci_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot; + int i, ret; + + chip = pci_get_drvdata(pdev); + if (!chip) + return 0; + + if (chip->fixes && chip->fixes->resume) { + ret = chip->fixes->resume(chip); + if (ret) + return ret; + } + + for (i = 0; i < chip->num_slots; i++) { + slot = chip->slots[i]; + if (!slot) + continue; + + ret = sdhci_runtime_resume_host(slot->host); + if (ret) + return ret; + } + + return 0; +} + +#else /* CONFIG_PM */ + +#define sdhci_pci_suspend NULL +#define sdhci_pci_resume NULL + +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops sdhci_pci_pm_ops = { + .suspend = sdhci_pci_suspend, + .resume = sdhci_pci_resume, + SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend, + sdhci_pci_runtime_resume, NULL) +}; + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + +static struct sdhci_pci_slot *sdhci_pci_probe_slot( + struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar, + int slotno) +{ + struct sdhci_pci_slot *slot; + struct sdhci_host *host; + int ret, bar = first_bar + slotno; + + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); + return ERR_PTR(-ENODEV); + } + + if (pci_resource_len(pdev, bar) < 0x100) { + dev_err(&pdev->dev, "Invalid iomem size. You may " + "experience problems.\n"); + } + + if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { + dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n"); + return ERR_PTR(-ENODEV); + } + + if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { + dev_err(&pdev->dev, "Unknown interface. Aborting.\n"); + return ERR_PTR(-ENODEV); + } + + host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot)); + if (IS_ERR(host)) { + dev_err(&pdev->dev, "cannot allocate host\n"); + return ERR_CAST(host); + } + + slot = sdhci_priv(host); + + slot->chip = chip; + slot->host = host; + slot->pci_bar = bar; + slot->rst_n_gpio = -EINVAL; + slot->cd_gpio = -EINVAL; + slot->cd_idx = -1; + + /* Retrieve platform data if there is any */ + if (*sdhci_pci_get_data) + slot->data = sdhci_pci_get_data(pdev, slotno); + + if (slot->data) { + if (slot->data->setup) { + ret = slot->data->setup(slot->data); + if (ret) { + dev_err(&pdev->dev, "platform setup failed\n"); + goto free; + } + } + slot->rst_n_gpio = slot->data->rst_n_gpio; + slot->cd_gpio = slot->data->cd_gpio; + } + + host->hw_name = "PCI"; + host->ops = &sdhci_pci_ops; + host->quirks = chip->quirks; + host->quirks2 = chip->quirks2; + + host->irq = pdev->irq; + + ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); + if (ret) { + dev_err(&pdev->dev, "cannot request region\n"); + goto cleanup; + } + + host->ioaddr = pci_ioremap_bar(pdev, bar); + if (!host->ioaddr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; + goto release; + } + + if (chip->fixes && chip->fixes->probe_slot) { + ret = chip->fixes->probe_slot(slot); + if (ret) + goto unmap; + } + + if (gpio_is_valid(slot->rst_n_gpio)) { + if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) { + gpio_direction_output(slot->rst_n_gpio, 1); + slot->host->mmc->caps |= MMC_CAP_HW_RESET; + slot->hw_reset = sdhci_pci_gpio_hw_reset; + } else { + dev_warn(&pdev->dev, "failed to request rst_n_gpio\n"); + slot->rst_n_gpio = -EINVAL; + } + } + + host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; + host->mmc->slotno = slotno; + host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; + + if (slot->cd_idx >= 0 && + mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx, + slot->cd_override_level, 0, NULL)) { + dev_warn(&pdev->dev, "failed to setup card detect gpio\n"); + slot->cd_idx = -1; + } + + ret = sdhci_add_host(host); + if (ret) + goto remove; + + sdhci_pci_add_own_cd(slot); + + /* + * Check if the chip needs a separate GPIO for card detect to wake up + * from runtime suspend. If it is not there, don't allow runtime PM. + * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. + */ + if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && + !gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0) + chip->allow_runtime_pm = false; + + return slot; + +remove: + if (gpio_is_valid(slot->rst_n_gpio)) + gpio_free(slot->rst_n_gpio); + + if (chip->fixes && chip->fixes->remove_slot) + chip->fixes->remove_slot(slot, 0); + +unmap: + iounmap(host->ioaddr); + +release: + pci_release_region(pdev, bar); + +cleanup: + if (slot->data && slot->data->cleanup) + slot->data->cleanup(slot->data); + +free: + sdhci_free_host(host); + + return ERR_PTR(ret); +} + +static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) +{ + int dead; + u32 scratch; + + sdhci_pci_remove_own_cd(slot); + + dead = 0; + scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + sdhci_remove_host(slot->host, dead); + + if (gpio_is_valid(slot->rst_n_gpio)) + gpio_free(slot->rst_n_gpio); + + if (slot->chip->fixes && slot->chip->fixes->remove_slot) + slot->chip->fixes->remove_slot(slot, dead); + + if (slot->data && slot->data->cleanup) + slot->data->cleanup(slot->data); + + pci_release_region(slot->chip->pdev, slot->pci_bar); + + sdhci_free_host(slot->host); +} + +static void sdhci_pci_runtime_pm_allow(struct device *dev) +{ + pm_runtime_put_noidle(dev); + pm_runtime_allow(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_suspend_ignore_children(dev, 1); +} + +static void sdhci_pci_runtime_pm_forbid(struct device *dev) +{ + pm_runtime_forbid(dev); + pm_runtime_get_noresume(dev); +} + +static int sdhci_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot; + + u8 slots, first_bar; + int ret, i; + + BUG_ON(pdev == NULL); + BUG_ON(ent == NULL); + + dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + + ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); + if (ret) + return ret; + + slots = PCI_SLOT_INFO_SLOTS(slots) + 1; + dev_dbg(&pdev->dev, "found %d slot(s)\n", slots); + if (slots == 0) + return -ENODEV; + + BUG_ON(slots > MAX_SLOTS); + + ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); + if (ret) + return ret; + + first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; + + if (first_bar > 5) { + dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n"); + return -ENODEV; + } + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto err; + } + + chip->pdev = pdev; + chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; + if (chip->fixes) { + chip->quirks = chip->fixes->quirks; + chip->quirks2 = chip->fixes->quirks2; + chip->allow_runtime_pm = chip->fixes->allow_runtime_pm; + } + chip->num_slots = slots; + + pci_set_drvdata(pdev, chip); + + if (chip->fixes && chip->fixes->probe) { + ret = chip->fixes->probe(chip); + if (ret) + goto free; + } + + slots = chip->num_slots; /* Quirk may have changed this */ + + for (i = 0; i < slots; i++) { + slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); + if (IS_ERR(slot)) { + for (i--; i >= 0; i--) + sdhci_pci_remove_slot(chip->slots[i]); + ret = PTR_ERR(slot); + goto free; + } + + chip->slots[i] = slot; + } + + if (chip->allow_runtime_pm) + sdhci_pci_runtime_pm_allow(&pdev->dev); + + return 0; + +free: + pci_set_drvdata(pdev, NULL); + kfree(chip); + +err: + pci_disable_device(pdev); + return ret; +} + +static void sdhci_pci_remove(struct pci_dev *pdev) +{ + int i; + struct sdhci_pci_chip *chip; + + chip = pci_get_drvdata(pdev); + + if (chip) { + if (chip->allow_runtime_pm) + sdhci_pci_runtime_pm_forbid(&pdev->dev); + + for (i = 0; i < chip->num_slots; i++) + sdhci_pci_remove_slot(chip->slots[i]); + + pci_set_drvdata(pdev, NULL); + kfree(chip); + } + + pci_disable_device(pdev); +} + +static struct pci_driver sdhci_driver = { + .name = "sdhci-pci", + .id_table = pci_ids, + .probe = sdhci_pci_probe, + .remove = sdhci_pci_remove, + .driver = { + .pm = &sdhci_pci_pm_ops + }, +}; + +module_pci_driver(sdhci_driver); + +MODULE_AUTHOR("Pierre Ossman "); +MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index e2ec108d..9a5e191 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -145,7 +145,6 @@ void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) scratch_32 |= 0x00080000; pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); } -EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init); int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) { @@ -179,7 +178,6 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) return 0; } -EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot); int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) { @@ -385,11 +383,9 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) return 0; } -EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe); int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) { sdhci_pci_o2_probe(chip); return 0; } -EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c deleted file mode 100644 index b3b0a3e..0000000 --- a/drivers/mmc/host/sdhci-pci.c +++ /dev/null @@ -1,1816 +0,0 @@ -/* linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface - * - * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * Thanks to the following companies for their support: - * - * - JMicron (hardware and technical support) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sdhci.h" -#include "sdhci-pci.h" -#include "sdhci-pci-o2micro.h" - -/*****************************************************************************\ - * * - * Hardware specific quirk handling * - * * -\*****************************************************************************/ - -static int ricoh_probe(struct sdhci_pci_chip *chip) -{ - if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || - chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) - chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; - return 0; -} - -static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->caps = - ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) - & SDHCI_TIMEOUT_CLK_MASK) | - - ((0x21 << SDHCI_CLOCK_BASE_SHIFT) - & SDHCI_CLOCK_BASE_MASK) | - - SDHCI_TIMEOUT_CLK_UNIT | - SDHCI_CAN_VDD_330 | - SDHCI_CAN_DO_HISPD | - SDHCI_CAN_DO_SDMA; - return 0; -} - -static int ricoh_mmc_resume(struct sdhci_pci_chip *chip) -{ - /* Apply a delay to allow controller to settle */ - /* Otherwise it becomes confused if card state changed - during suspend */ - msleep(500); - return 0; -} - -static const struct sdhci_pci_fixes sdhci_ricoh = { - .probe = ricoh_probe, - .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | - SDHCI_QUIRK_FORCE_DMA | - SDHCI_QUIRK_CLOCK_BEFORE_RESET, -}; - -static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { - .probe_slot = ricoh_mmc_probe_slot, - .resume = ricoh_mmc_resume, - .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | - SDHCI_QUIRK_CLOCK_BEFORE_RESET | - SDHCI_QUIRK_NO_CARD_NO_RESET | - SDHCI_QUIRK_MISSING_CAPS -}; - -static const struct sdhci_pci_fixes sdhci_ene_712 = { - .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_BROKEN_DMA, -}; - -static const struct sdhci_pci_fixes sdhci_ene_714 = { - .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | - SDHCI_QUIRK_BROKEN_DMA, -}; - -static const struct sdhci_pci_fixes sdhci_cafe = { - .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | - SDHCI_QUIRK_NO_BUSY_IRQ | - SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, -}; - -static const struct sdhci_pci_fixes sdhci_intel_qrk = { - .quirks = SDHCI_QUIRK_NO_HISPD_BIT, -}; - -static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; - return 0; -} - -/* - * ADMA operation is disabled for Moorestown platform due to - * hardware bugs. - */ -static int mrst_hc_probe(struct sdhci_pci_chip *chip) -{ - /* - * slots number is fixed here for MRST as SDIO3/5 are never used and - * have hardware bugs. - */ - chip->num_slots = 1; - return 0; -} - -static int pch_hc_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; - return 0; -} - -#ifdef CONFIG_PM - -static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id) -{ - struct sdhci_pci_slot *slot = dev_id; - struct sdhci_host *host = slot->host; - - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); - return IRQ_HANDLED; -} - -static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) -{ - int err, irq, gpio = slot->cd_gpio; - - slot->cd_gpio = -EINVAL; - slot->cd_irq = -EINVAL; - - if (!gpio_is_valid(gpio)) - return; - - err = gpio_request(gpio, "sd_cd"); - if (err < 0) - goto out; - - err = gpio_direction_input(gpio); - if (err < 0) - goto out_free; - - irq = gpio_to_irq(gpio); - if (irq < 0) - goto out_free; - - err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, "sd_cd", slot); - if (err) - goto out_free; - - slot->cd_gpio = gpio; - slot->cd_irq = irq; - - return; - -out_free: - gpio_free(gpio); -out: - dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n"); -} - -static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) -{ - if (slot->cd_irq >= 0) - free_irq(slot->cd_irq, slot); - if (gpio_is_valid(slot->cd_gpio)) - gpio_free(slot->cd_gpio); -} - -#else - -static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) -{ -} - -static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) -{ -} - -#endif - -static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; - slot->host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC | - MMC_CAP2_HC_ERASE_SZ; - return 0; -} - -static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE; - return 0; -} - -static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { - .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, - .probe_slot = mrst_hc_probe_slot, -}; - -static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = { - .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, - .probe = mrst_hc_probe, -}; - -static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .allow_runtime_pm = true, - .own_cd_for_runtime_pm = true, -}; - -static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, - .allow_runtime_pm = true, - .probe_slot = mfd_sdio_probe_slot, -}; - -static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .allow_runtime_pm = true, - .probe_slot = mfd_emmc_probe_slot, -}; - -static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { - .quirks = SDHCI_QUIRK_BROKEN_ADMA, - .probe_slot = pch_hc_probe_slot, -}; - -static void sdhci_pci_int_hw_reset(struct sdhci_host *host) -{ - u8 reg; - - reg = sdhci_readb(host, SDHCI_POWER_CONTROL); - reg |= 0x10; - sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); - /* For eMMC, minimum is 1us but give it 9us for good measure */ - udelay(9); - reg &= ~0x10; - sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); - /* For eMMC, minimum is 200us but give it 300us for good measure */ - usleep_range(300, 1000); -} - -static int spt_select_drive_strength(struct sdhci_host *host, - struct mmc_card *card, - unsigned int max_dtr, - int host_drv, int card_drv, int *drv_type) -{ - int drive_strength; - - if (sdhci_pci_spt_drive_strength > 0) - drive_strength = sdhci_pci_spt_drive_strength & 0xf; - else - drive_strength = 1; /* 33-ohm */ - - if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0) - drive_strength = 0; /* Default 50-ohm */ - - return drive_strength; -} - -/* Try to read the drive strength from the card */ -static void spt_read_drive_strength(struct sdhci_host *host) -{ - u32 val, i, t; - u16 m; - - if (sdhci_pci_spt_drive_strength) - return; - - sdhci_pci_spt_drive_strength = -1; - - m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7; - if (m != 3 && m != 5) - return; - val = sdhci_readl(host, SDHCI_PRESENT_STATE); - if (val & 0x3) - return; - sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE); - sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); - sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE); - sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); - sdhci_writew(host, 512, SDHCI_BLOCK_SIZE); - sdhci_writew(host, 1, SDHCI_BLOCK_COUNT); - sdhci_writel(host, 0, SDHCI_ARGUMENT); - sdhci_writew(host, 0x83b, SDHCI_COMMAND); - for (i = 0; i < 1000; i++) { - val = sdhci_readl(host, SDHCI_INT_STATUS); - if (val & 0xffff8000) - return; - if (val & 0x20) - break; - udelay(1); - } - val = sdhci_readl(host, SDHCI_PRESENT_STATE); - if (!(val & 0x800)) - return; - for (i = 0; i < 47; i++) - val = sdhci_readl(host, SDHCI_BUFFER); - t = val & 0xf00; - if (t != 0x200 && t != 0x300) - return; - - sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf); -} - -static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | - MMC_CAP_BUS_WIDTH_TEST | - MMC_CAP_WAIT_WHILE_BUSY; - slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; - slot->hw_reset = sdhci_pci_int_hw_reset; - if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) - slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ - if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) { - spt_read_drive_strength(slot->host); - slot->select_drive_strength = spt_select_drive_strength; - } - return 0; -} - -static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE | - MMC_CAP_BUS_WIDTH_TEST | - MMC_CAP_WAIT_WHILE_BUSY; - return 0; -} - -static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST | - MMC_CAP_WAIT_WHILE_BUSY; - slot->cd_con_id = NULL; - slot->cd_idx = 0; - slot->cd_override_level = true; - return 0; -} - -static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { - .allow_runtime_pm = true, - .probe_slot = byt_emmc_probe_slot, - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | - SDHCI_QUIRK2_STOP_WITH_TC, -}; - -static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | - SDHCI_QUIRK2_PRESET_VALUE_BROKEN, - .allow_runtime_pm = true, - .probe_slot = byt_sdio_probe_slot, -}; - -static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | - SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_STOP_WITH_TC, - .allow_runtime_pm = true, - .own_cd_for_runtime_pm = true, - .probe_slot = byt_sd_probe_slot, -}; - -/* Define Host controllers for Intel Merrifield platform */ -#define INTEL_MRFL_EMMC_0 0 -#define INTEL_MRFL_EMMC_1 1 - -static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) -{ - if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) && - (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1)) - /* SD support is not ready yet */ - return -ENODEV; - - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_1_8V_DDR; - - return 0; -} - -static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | - SDHCI_QUIRK2_PRESET_VALUE_BROKEN, - .allow_runtime_pm = true, - .probe_slot = intel_mrfl_mmc_probe_slot, -}; - -/* O2Micro extra registers */ -#define O2_SD_LOCK_WP 0xD3 -#define O2_SD_MULTI_VCC3V 0xEE -#define O2_SD_CLKREQ 0xEC -#define O2_SD_CAPS 0xE0 -#define O2_SD_ADMA1 0xE2 -#define O2_SD_ADMA2 0xE7 -#define O2_SD_INF_MOD 0xF1 - -static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) -{ - u8 scratch; - int ret; - - ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch); - if (ret) - return ret; - - /* - * Turn PMOS on [bit 0], set over current detection to 2.4 V - * [bit 1:2] and enable over current debouncing [bit 6]. - */ - if (on) - scratch |= 0x47; - else - scratch &= ~0x47; - - ret = pci_write_config_byte(chip->pdev, 0xAE, scratch); - if (ret) - return ret; - - return 0; -} - -static int jmicron_probe(struct sdhci_pci_chip *chip) -{ - int ret; - u16 mmcdev = 0; - - if (chip->pdev->revision == 0) { - chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | - SDHCI_QUIRK_32BIT_DMA_SIZE | - SDHCI_QUIRK_32BIT_ADMA_SIZE | - SDHCI_QUIRK_RESET_AFTER_REQUEST | - SDHCI_QUIRK_BROKEN_SMALL_PIO; - } - - /* - * JMicron chips can have two interfaces to the same hardware - * in order to work around limitations in Microsoft's driver. - * We need to make sure we only bind to one of them. - * - * This code assumes two things: - * - * 1. The PCI code adds subfunctions in order. - * - * 2. The MMC interface has a lower subfunction number - * than the SD interface. - */ - if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) - mmcdev = PCI_DEVICE_ID_JMICRON_JMB38X_MMC; - else if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD) - mmcdev = PCI_DEVICE_ID_JMICRON_JMB388_ESD; - - if (mmcdev) { - struct pci_dev *sd_dev; - - sd_dev = NULL; - while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON, - mmcdev, sd_dev)) != NULL) { - if ((PCI_SLOT(chip->pdev->devfn) == - PCI_SLOT(sd_dev->devfn)) && - (chip->pdev->bus == sd_dev->bus)) - break; - } - - if (sd_dev) { - pci_dev_put(sd_dev); - dev_info(&chip->pdev->dev, "Refusing to bind to " - "secondary interface.\n"); - return -ENODEV; - } - } - - /* - * JMicron chips need a bit of a nudge to enable the power - * output pins. - */ - ret = jmicron_pmos(chip, 1); - if (ret) { - dev_err(&chip->pdev->dev, "Failure enabling card power\n"); - return ret; - } - - /* quirk for unsable RO-detection on JM388 chips */ - if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD || - chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) - chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT; - - return 0; -} - -static void jmicron_enable_mmc(struct sdhci_host *host, int on) -{ - u8 scratch; - - scratch = readb(host->ioaddr + 0xC0); - - if (on) - scratch |= 0x01; - else - scratch &= ~0x01; - - writeb(scratch, host->ioaddr + 0xC0); -} - -static int jmicron_probe_slot(struct sdhci_pci_slot *slot) -{ - if (slot->chip->pdev->revision == 0) { - u16 version; - - version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION); - version = (version & SDHCI_VENDOR_VER_MASK) >> - SDHCI_VENDOR_VER_SHIFT; - - /* - * Older versions of the chip have lots of nasty glitches - * in the ADMA engine. It's best just to avoid it - * completely. - */ - if (version < 0xAC) - slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; - } - - /* JM388 MMC doesn't support 1.8V while SD supports it */ - if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { - slot->host->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34 | - MMC_VDD_29_30 | MMC_VDD_30_31 | - MMC_VDD_165_195; /* allow 1.8V */ - slot->host->ocr_avail_mmc = MMC_VDD_32_33 | MMC_VDD_33_34 | - MMC_VDD_29_30 | MMC_VDD_30_31; /* no 1.8V for MMC */ - } - - /* - * The secondary interface requires a bit set to get the - * interrupts. - */ - if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || - slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) - jmicron_enable_mmc(slot->host, 1); - - slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST; - - return 0; -} - -static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead) -{ - if (dead) - return; - - if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || - slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) - jmicron_enable_mmc(slot->host, 0); -} - -static int jmicron_suspend(struct sdhci_pci_chip *chip) -{ - int i; - - if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || - chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { - for (i = 0; i < chip->num_slots; i++) - jmicron_enable_mmc(chip->slots[i]->host, 0); - } - - return 0; -} - -static int jmicron_resume(struct sdhci_pci_chip *chip) -{ - int ret, i; - - if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || - chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { - for (i = 0; i < chip->num_slots; i++) - jmicron_enable_mmc(chip->slots[i]->host, 1); - } - - ret = jmicron_pmos(chip, 1); - if (ret) { - dev_err(&chip->pdev->dev, "Failure enabling card power\n"); - return ret; - } - - return 0; -} - -static const struct sdhci_pci_fixes sdhci_o2 = { - .probe = sdhci_pci_o2_probe, - .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, - .probe_slot = sdhci_pci_o2_probe_slot, - .resume = sdhci_pci_o2_resume, -}; - -static const struct sdhci_pci_fixes sdhci_jmicron = { - .probe = jmicron_probe, - - .probe_slot = jmicron_probe_slot, - .remove_slot = jmicron_remove_slot, - - .suspend = jmicron_suspend, - .resume = jmicron_resume, -}; - -/* SysKonnect CardBus2SDIO extra registers */ -#define SYSKT_CTRL 0x200 -#define SYSKT_RDFIFO_STAT 0x204 -#define SYSKT_WRFIFO_STAT 0x208 -#define SYSKT_POWER_DATA 0x20c -#define SYSKT_POWER_330 0xef -#define SYSKT_POWER_300 0xf8 -#define SYSKT_POWER_184 0xcc -#define SYSKT_POWER_CMD 0x20d -#define SYSKT_POWER_START (1 << 7) -#define SYSKT_POWER_STATUS 0x20e -#define SYSKT_POWER_STATUS_OK (1 << 0) -#define SYSKT_BOARD_REV 0x210 -#define SYSKT_CHIP_REV 0x211 -#define SYSKT_CONF_DATA 0x212 -#define SYSKT_CONF_DATA_1V8 (1 << 2) -#define SYSKT_CONF_DATA_2V5 (1 << 1) -#define SYSKT_CONF_DATA_3V3 (1 << 0) - -static int syskt_probe(struct sdhci_pci_chip *chip) -{ - if ((chip->pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { - chip->pdev->class &= ~0x0000FF; - chip->pdev->class |= PCI_SDHCI_IFDMA; - } - return 0; -} - -static int syskt_probe_slot(struct sdhci_pci_slot *slot) -{ - int tm, ps; - - u8 board_rev = readb(slot->host->ioaddr + SYSKT_BOARD_REV); - u8 chip_rev = readb(slot->host->ioaddr + SYSKT_CHIP_REV); - dev_info(&slot->chip->pdev->dev, "SysKonnect CardBus2SDIO, " - "board rev %d.%d, chip rev %d.%d\n", - board_rev >> 4, board_rev & 0xf, - chip_rev >> 4, chip_rev & 0xf); - if (chip_rev >= 0x20) - slot->host->quirks |= SDHCI_QUIRK_FORCE_DMA; - - writeb(SYSKT_POWER_330, slot->host->ioaddr + SYSKT_POWER_DATA); - writeb(SYSKT_POWER_START, slot->host->ioaddr + SYSKT_POWER_CMD); - udelay(50); - tm = 10; /* Wait max 1 ms */ - do { - ps = readw(slot->host->ioaddr + SYSKT_POWER_STATUS); - if (ps & SYSKT_POWER_STATUS_OK) - break; - udelay(100); - } while (--tm); - if (!tm) { - dev_err(&slot->chip->pdev->dev, - "power regulator never stabilized"); - writeb(0, slot->host->ioaddr + SYSKT_POWER_CMD); - return -ENODEV; - } - - return 0; -} - -static const struct sdhci_pci_fixes sdhci_syskt = { - .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER, - .probe = syskt_probe, - .probe_slot = syskt_probe_slot, -}; - -static int via_probe(struct sdhci_pci_chip *chip) -{ - if (chip->pdev->revision == 0x10) - chip->quirks |= SDHCI_QUIRK_DELAY_AFTER_POWER; - - return 0; -} - -static const struct sdhci_pci_fixes sdhci_via = { - .probe = via_probe, -}; - -static int rtsx_probe_slot(struct sdhci_pci_slot *slot) -{ - slot->host->mmc->caps2 |= MMC_CAP2_HS200; - return 0; -} - -static const struct sdhci_pci_fixes sdhci_rtsx = { - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_BROKEN_64_BIT_DMA | - SDHCI_QUIRK2_BROKEN_DDR50, - .probe_slot = rtsx_probe_slot, -}; - -/*AMD chipset generation*/ -enum amd_chipset_gen { - AMD_CHIPSET_BEFORE_ML, - AMD_CHIPSET_CZ, - AMD_CHIPSET_NL, - AMD_CHIPSET_UNKNOWN, -}; - -static int amd_probe(struct sdhci_pci_chip *chip) -{ - struct pci_dev *smbus_dev; - enum amd_chipset_gen gen; - - smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, - PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); - if (smbus_dev) { - gen = AMD_CHIPSET_BEFORE_ML; - } else { - smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, - PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL); - if (smbus_dev) { - if (smbus_dev->revision < 0x51) - gen = AMD_CHIPSET_CZ; - else - gen = AMD_CHIPSET_NL; - } else { - gen = AMD_CHIPSET_UNKNOWN; - } - } - - if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) { - chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; - chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; - } - - return 0; -} - -static const struct sdhci_pci_fixes sdhci_amd = { - .probe = amd_probe, -}; - -static const struct pci_device_id pci_ids[] = { - { - .vendor = PCI_VENDOR_ID_RICOH, - .device = PCI_DEVICE_ID_RICOH_R5C822, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ricoh, - }, - - { - .vendor = PCI_VENDOR_ID_RICOH, - .device = 0x843, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, - }, - - { - .vendor = PCI_VENDOR_ID_RICOH, - .device = 0xe822, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, - }, - - { - .vendor = PCI_VENDOR_ID_RICOH, - .device = 0xe823, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, - }, - - { - .vendor = PCI_VENDOR_ID_ENE, - .device = PCI_DEVICE_ID_ENE_CB712_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ene_712, - }, - - { - .vendor = PCI_VENDOR_ID_ENE, - .device = PCI_DEVICE_ID_ENE_CB712_SD_2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ene_712, - }, - - { - .vendor = PCI_VENDOR_ID_ENE, - .device = PCI_DEVICE_ID_ENE_CB714_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ene_714, - }, - - { - .vendor = PCI_VENDOR_ID_ENE, - .device = PCI_DEVICE_ID_ENE_CB714_SD_2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_ene_714, - }, - - { - .vendor = PCI_VENDOR_ID_MARVELL, - .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_cafe, - }, - - { - .vendor = PCI_VENDOR_ID_JMICRON, - .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_jmicron, - }, - - { - .vendor = PCI_VENDOR_ID_JMICRON, - .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_jmicron, - }, - - { - .vendor = PCI_VENDOR_ID_JMICRON, - .device = PCI_DEVICE_ID_JMICRON_JMB388_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_jmicron, - }, - - { - .vendor = PCI_VENDOR_ID_JMICRON, - .device = PCI_DEVICE_ID_JMICRON_JMB388_ESD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_jmicron, - }, - - { - .vendor = PCI_VENDOR_ID_SYSKONNECT, - .device = 0x8000, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_syskt, - }, - - { - .vendor = PCI_VENDOR_ID_VIA, - .device = 0x95d0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_via, - }, - - { - .vendor = PCI_VENDOR_ID_REALTEK, - .device = 0x5250, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_rtsx, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_QRK_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_qrk, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MRST_SD0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc0, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MRST_SD1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1_hc2, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MRST_SD2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1_hc2, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MFD_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MFD_SDIO1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MFD_SDIO2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MFD_EMMC0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MFD_EMMC1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_PCH_SDIO0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_PCH_SDIO1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BYT_EMMC, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BYT_SDIO, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BYT_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BYT_EMMC2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BSW_EMMC, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BSW_SDIO, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_BSW_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CLV_SDIO0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CLV_SDIO1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CLV_SDIO2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CLV_EMMC0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CLV_EMMC1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MRFL_MMC, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_SPT_EMMC, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_SPT_SDIO, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, - }, - - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_SPT_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_8120, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_8220, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_8221, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_8320, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_8321, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_FUJIN2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_SDS0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_SDS1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_SEABIRD0, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - - { - .vendor = PCI_VENDOR_ID_O2, - .device = PCI_DEVICE_ID_O2_SEABIRD1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_o2, - }, - { - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_ANY_ID, - .class = PCI_CLASS_SYSTEM_SDHCI << 8, - .class_mask = 0xFFFF00, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_amd, - }, - { /* Generic SD host controller */ - PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) - }, - - { /* end: all zeroes */ }, -}; - -MODULE_DEVICE_TABLE(pci, pci_ids); - -/*****************************************************************************\ - * * - * SDHCI core callbacks * - * * -\*****************************************************************************/ - -static int sdhci_pci_enable_dma(struct sdhci_host *host) -{ - struct sdhci_pci_slot *slot; - struct pci_dev *pdev; - int ret = -1; - - slot = sdhci_priv(host); - pdev = slot->chip->pdev; - - if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && - ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && - (host->flags & SDHCI_USE_SDMA)) { - dev_warn(&pdev->dev, "Will use DMA mode even though HW " - "doesn't fully claim to support it.\n"); - } - - if (host->flags & SDHCI_USE_64_BIT_DMA) { - if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) { - host->flags &= ~SDHCI_USE_64_BIT_DMA; - } else { - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (ret) - dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n"); - } - } - if (ret) - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - pci_set_master(pdev); - - return 0; -} - -static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width) -{ - u8 ctrl; - - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); - - switch (width) { - case MMC_BUS_WIDTH_8: - ctrl |= SDHCI_CTRL_8BITBUS; - ctrl &= ~SDHCI_CTRL_4BITBUS; - break; - case MMC_BUS_WIDTH_4: - ctrl |= SDHCI_CTRL_4BITBUS; - ctrl &= ~SDHCI_CTRL_8BITBUS; - break; - default: - ctrl &= ~(SDHCI_CTRL_8BITBUS | SDHCI_CTRL_4BITBUS); - break; - } - - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); -} - -static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) -{ - struct sdhci_pci_slot *slot = sdhci_priv(host); - int rst_n_gpio = slot->rst_n_gpio; - - if (!gpio_is_valid(rst_n_gpio)) - return; - gpio_set_value_cansleep(rst_n_gpio, 0); - /* For eMMC, minimum is 1us but give it 10us for good measure */ - udelay(10); - gpio_set_value_cansleep(rst_n_gpio, 1); - /* For eMMC, minimum is 200us but give it 300us for good measure */ - usleep_range(300, 1000); -} - -static void sdhci_pci_hw_reset(struct sdhci_host *host) -{ - struct sdhci_pci_slot *slot = sdhci_priv(host); - - if (slot->hw_reset) - slot->hw_reset(host); -} - -static int sdhci_pci_select_drive_strength(struct sdhci_host *host, - struct mmc_card *card, - unsigned int max_dtr, int host_drv, - int card_drv, int *drv_type) -{ - struct sdhci_pci_slot *slot = sdhci_priv(host); - - if (!slot->select_drive_strength) - return 0; - - return slot->select_drive_strength(host, card, max_dtr, host_drv, - card_drv, drv_type); -} - -static const struct sdhci_ops sdhci_pci_ops = { - .set_clock = sdhci_set_clock, - .enable_dma = sdhci_pci_enable_dma, - .set_bus_width = sdhci_pci_set_bus_width, - .reset = sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, - .hw_reset = sdhci_pci_hw_reset, - .select_drive_strength = sdhci_pci_select_drive_strength, -}; - -/*****************************************************************************\ - * * - * Suspend/resume * - * * -\*****************************************************************************/ - -#ifdef CONFIG_PM - -static int sdhci_pci_suspend(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct sdhci_pci_chip *chip; - struct sdhci_pci_slot *slot; - mmc_pm_flag_t slot_pm_flags; - mmc_pm_flag_t pm_flags = 0; - int i, ret; - - chip = pci_get_drvdata(pdev); - if (!chip) - return 0; - - for (i = 0; i < chip->num_slots; i++) { - slot = chip->slots[i]; - if (!slot) - continue; - - ret = sdhci_suspend_host(slot->host); - - if (ret) - goto err_pci_suspend; - - slot_pm_flags = slot->host->mmc->pm_flags; - if (slot_pm_flags & MMC_PM_WAKE_SDIO_IRQ) - sdhci_enable_irq_wakeups(slot->host); - - pm_flags |= slot_pm_flags; - } - - if (chip->fixes && chip->fixes->suspend) { - ret = chip->fixes->suspend(chip); - if (ret) - goto err_pci_suspend; - } - - if (pm_flags & MMC_PM_KEEP_POWER) { - if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) - device_init_wakeup(dev, true); - else - device_init_wakeup(dev, false); - } else - device_init_wakeup(dev, false); - - return 0; - -err_pci_suspend: - while (--i >= 0) - sdhci_resume_host(chip->slots[i]->host); - return ret; -} - -static int sdhci_pci_resume(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct sdhci_pci_chip *chip; - struct sdhci_pci_slot *slot; - int i, ret; - - chip = pci_get_drvdata(pdev); - if (!chip) - return 0; - - if (chip->fixes && chip->fixes->resume) { - ret = chip->fixes->resume(chip); - if (ret) - return ret; - } - - for (i = 0; i < chip->num_slots; i++) { - slot = chip->slots[i]; - if (!slot) - continue; - - ret = sdhci_resume_host(slot->host); - if (ret) - return ret; - } - - return 0; -} - -static int sdhci_pci_runtime_suspend(struct device *dev) -{ - struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); - struct sdhci_pci_chip *chip; - struct sdhci_pci_slot *slot; - int i, ret; - - chip = pci_get_drvdata(pdev); - if (!chip) - return 0; - - for (i = 0; i < chip->num_slots; i++) { - slot = chip->slots[i]; - if (!slot) - continue; - - ret = sdhci_runtime_suspend_host(slot->host); - - if (ret) - goto err_pci_runtime_suspend; - } - - if (chip->fixes && chip->fixes->suspend) { - ret = chip->fixes->suspend(chip); - if (ret) - goto err_pci_runtime_suspend; - } - - return 0; - -err_pci_runtime_suspend: - while (--i >= 0) - sdhci_runtime_resume_host(chip->slots[i]->host); - return ret; -} - -static int sdhci_pci_runtime_resume(struct device *dev) -{ - struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); - struct sdhci_pci_chip *chip; - struct sdhci_pci_slot *slot; - int i, ret; - - chip = pci_get_drvdata(pdev); - if (!chip) - return 0; - - if (chip->fixes && chip->fixes->resume) { - ret = chip->fixes->resume(chip); - if (ret) - return ret; - } - - for (i = 0; i < chip->num_slots; i++) { - slot = chip->slots[i]; - if (!slot) - continue; - - ret = sdhci_runtime_resume_host(slot->host); - if (ret) - return ret; - } - - return 0; -} - -#else /* CONFIG_PM */ - -#define sdhci_pci_suspend NULL -#define sdhci_pci_resume NULL - -#endif /* CONFIG_PM */ - -static const struct dev_pm_ops sdhci_pci_pm_ops = { - .suspend = sdhci_pci_suspend, - .resume = sdhci_pci_resume, - SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend, - sdhci_pci_runtime_resume, NULL) -}; - -/*****************************************************************************\ - * * - * Device probing/removal * - * * -\*****************************************************************************/ - -static struct sdhci_pci_slot *sdhci_pci_probe_slot( - struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar, - int slotno) -{ - struct sdhci_pci_slot *slot; - struct sdhci_host *host; - int ret, bar = first_bar + slotno; - - if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { - dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); - return ERR_PTR(-ENODEV); - } - - if (pci_resource_len(pdev, bar) < 0x100) { - dev_err(&pdev->dev, "Invalid iomem size. You may " - "experience problems.\n"); - } - - if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { - dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n"); - return ERR_PTR(-ENODEV); - } - - if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { - dev_err(&pdev->dev, "Unknown interface. Aborting.\n"); - return ERR_PTR(-ENODEV); - } - - host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot)); - if (IS_ERR(host)) { - dev_err(&pdev->dev, "cannot allocate host\n"); - return ERR_CAST(host); - } - - slot = sdhci_priv(host); - - slot->chip = chip; - slot->host = host; - slot->pci_bar = bar; - slot->rst_n_gpio = -EINVAL; - slot->cd_gpio = -EINVAL; - slot->cd_idx = -1; - - /* Retrieve platform data if there is any */ - if (*sdhci_pci_get_data) - slot->data = sdhci_pci_get_data(pdev, slotno); - - if (slot->data) { - if (slot->data->setup) { - ret = slot->data->setup(slot->data); - if (ret) { - dev_err(&pdev->dev, "platform setup failed\n"); - goto free; - } - } - slot->rst_n_gpio = slot->data->rst_n_gpio; - slot->cd_gpio = slot->data->cd_gpio; - } - - host->hw_name = "PCI"; - host->ops = &sdhci_pci_ops; - host->quirks = chip->quirks; - host->quirks2 = chip->quirks2; - - host->irq = pdev->irq; - - ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); - if (ret) { - dev_err(&pdev->dev, "cannot request region\n"); - goto cleanup; - } - - host->ioaddr = pci_ioremap_bar(pdev, bar); - if (!host->ioaddr) { - dev_err(&pdev->dev, "failed to remap registers\n"); - ret = -ENOMEM; - goto release; - } - - if (chip->fixes && chip->fixes->probe_slot) { - ret = chip->fixes->probe_slot(slot); - if (ret) - goto unmap; - } - - if (gpio_is_valid(slot->rst_n_gpio)) { - if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) { - gpio_direction_output(slot->rst_n_gpio, 1); - slot->host->mmc->caps |= MMC_CAP_HW_RESET; - slot->hw_reset = sdhci_pci_gpio_hw_reset; - } else { - dev_warn(&pdev->dev, "failed to request rst_n_gpio\n"); - slot->rst_n_gpio = -EINVAL; - } - } - - host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; - host->mmc->slotno = slotno; - host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; - - if (slot->cd_idx >= 0 && - mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx, - slot->cd_override_level, 0, NULL)) { - dev_warn(&pdev->dev, "failed to setup card detect gpio\n"); - slot->cd_idx = -1; - } - - ret = sdhci_add_host(host); - if (ret) - goto remove; - - sdhci_pci_add_own_cd(slot); - - /* - * Check if the chip needs a separate GPIO for card detect to wake up - * from runtime suspend. If it is not there, don't allow runtime PM. - * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. - */ - if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && - !gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0) - chip->allow_runtime_pm = false; - - return slot; - -remove: - if (gpio_is_valid(slot->rst_n_gpio)) - gpio_free(slot->rst_n_gpio); - - if (chip->fixes && chip->fixes->remove_slot) - chip->fixes->remove_slot(slot, 0); - -unmap: - iounmap(host->ioaddr); - -release: - pci_release_region(pdev, bar); - -cleanup: - if (slot->data && slot->data->cleanup) - slot->data->cleanup(slot->data); - -free: - sdhci_free_host(host); - - return ERR_PTR(ret); -} - -static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) -{ - int dead; - u32 scratch; - - sdhci_pci_remove_own_cd(slot); - - dead = 0; - scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); - if (scratch == (u32)-1) - dead = 1; - - sdhci_remove_host(slot->host, dead); - - if (gpio_is_valid(slot->rst_n_gpio)) - gpio_free(slot->rst_n_gpio); - - if (slot->chip->fixes && slot->chip->fixes->remove_slot) - slot->chip->fixes->remove_slot(slot, dead); - - if (slot->data && slot->data->cleanup) - slot->data->cleanup(slot->data); - - pci_release_region(slot->chip->pdev, slot->pci_bar); - - sdhci_free_host(slot->host); -} - -static void sdhci_pci_runtime_pm_allow(struct device *dev) -{ - pm_runtime_put_noidle(dev); - pm_runtime_allow(dev); - pm_runtime_set_autosuspend_delay(dev, 50); - pm_runtime_use_autosuspend(dev); - pm_suspend_ignore_children(dev, 1); -} - -static void sdhci_pci_runtime_pm_forbid(struct device *dev) -{ - pm_runtime_forbid(dev); - pm_runtime_get_noresume(dev); -} - -static int sdhci_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct sdhci_pci_chip *chip; - struct sdhci_pci_slot *slot; - - u8 slots, first_bar; - int ret, i; - - BUG_ON(pdev == NULL); - BUG_ON(ent == NULL); - - dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n", - (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); - - ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); - if (ret) - return ret; - - slots = PCI_SLOT_INFO_SLOTS(slots) + 1; - dev_dbg(&pdev->dev, "found %d slot(s)\n", slots); - if (slots == 0) - return -ENODEV; - - BUG_ON(slots > MAX_SLOTS); - - ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); - if (ret) - return ret; - - first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; - - if (first_bar > 5) { - dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n"); - return -ENODEV; - } - - ret = pci_enable_device(pdev); - if (ret) - return ret; - - chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL); - if (!chip) { - ret = -ENOMEM; - goto err; - } - - chip->pdev = pdev; - chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; - if (chip->fixes) { - chip->quirks = chip->fixes->quirks; - chip->quirks2 = chip->fixes->quirks2; - chip->allow_runtime_pm = chip->fixes->allow_runtime_pm; - } - chip->num_slots = slots; - - pci_set_drvdata(pdev, chip); - - if (chip->fixes && chip->fixes->probe) { - ret = chip->fixes->probe(chip); - if (ret) - goto free; - } - - slots = chip->num_slots; /* Quirk may have changed this */ - - for (i = 0; i < slots; i++) { - slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); - if (IS_ERR(slot)) { - for (i--; i >= 0; i--) - sdhci_pci_remove_slot(chip->slots[i]); - ret = PTR_ERR(slot); - goto free; - } - - chip->slots[i] = slot; - } - - if (chip->allow_runtime_pm) - sdhci_pci_runtime_pm_allow(&pdev->dev); - - return 0; - -free: - pci_set_drvdata(pdev, NULL); - kfree(chip); - -err: - pci_disable_device(pdev); - return ret; -} - -static void sdhci_pci_remove(struct pci_dev *pdev) -{ - int i; - struct sdhci_pci_chip *chip; - - chip = pci_get_drvdata(pdev); - - if (chip) { - if (chip->allow_runtime_pm) - sdhci_pci_runtime_pm_forbid(&pdev->dev); - - for (i = 0; i < chip->num_slots; i++) - sdhci_pci_remove_slot(chip->slots[i]); - - pci_set_drvdata(pdev, NULL); - kfree(chip); - } - - pci_disable_device(pdev); -} - -static struct pci_driver sdhci_driver = { - .name = "sdhci-pci", - .id_table = pci_ids, - .probe = sdhci_pci_probe, - .remove = sdhci_pci_remove, - .driver = { - .pm = &sdhci_pci_pm_ops - }, -}; - -module_pci_driver(sdhci_driver); - -MODULE_AUTHOR("Pierre Ossman "); -MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From f0cbd7806d736d77714994cc41c5161c0a29cccf Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 5 Oct 2015 20:31:58 +0100 Subject: mmc: sdhci-pci: Make sdhci_pci_o2_fujin2_pci_init() static It has no external callers. Signed-off-by: Ben Hutchings Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 9a5e191..d48f031 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -60,7 +60,7 @@ static void o2_pci_led_enable(struct sdhci_pci_chip *chip) } -void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) +static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) { u32 scratch_32; int ret; diff --git a/drivers/mmc/host/sdhci-pci-o2micro.h b/drivers/mmc/host/sdhci-pci-o2micro.h index f7ffc90..770f538 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.h +++ b/drivers/mmc/host/sdhci-pci-o2micro.h @@ -64,8 +64,6 @@ #define O2_SD_VENDOR_SETTING 0x110 #define O2_SD_VENDOR_SETTING2 0x1C8 -extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip); - extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot); extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip); -- cgit v0.10.2 From 7582041ff3d4aaa348894858d6384c4a9981c5fc Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 6 Oct 2015 04:01:04 +0800 Subject: mmc: sdhci-pci: fix simple_return.cocci warnings drivers/mmc/host/sdhci-pci-core.c:447:1-4: WARNING: end returns can be simpified Simplify a trivial if-return sequence. Possibly combine with a preceding function call. Generated by: scripts/coccinelle/misc/simple_return.cocci CC: Ben Hutchings Signed-off-by: Fengguang Wu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index b3b0a3e..6d86dc8 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -444,11 +444,7 @@ static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) else scratch &= ~0x47; - ret = pci_write_config_byte(chip->pdev, 0xAE, scratch); - if (ret) - return ret; - - return 0; + return pci_write_config_byte(chip->pdev, 0xAE, scratch); } static int jmicron_probe(struct sdhci_pci_chip *chip) -- cgit v0.10.2 From f0232063fb64fc79bfb7b6b35d611b2e4a79ffe6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 30 Sep 2015 16:07:37 +0200 Subject: clk: rockchip: Allow more precision for some mmc clock phases Because of the inexact nature of the extra MMC delay elements (it's not possible to keep the phase monotonic and to also make phases (mod 90) > 70), we previously only allowed phases (mod 90) of 22.5, 45, and 67.5. But it's not the end of the world if the MMC clock phase goes non-monotonic. At most we'll be 25 degrees off. It's way better to test more phases to look for bad ones than to be 25 degrees off, because in the case of MMC really the point is to find bad phases and get as far asway from the as possible. If we get to test extra phases by going slightly non-monotonic then that might be fine. Worst case we would end up at a phases that's slight differnt than the one we wanted, but at least we'd still be quite far away from the a bad phase. Signed-off-by: Douglas Anderson Fold in more precise variance-values of 44-77 instead of 40-80. Fold in the actual removal of the monotonic requirement and adapt patch message accordingly. Signed-off-by: Heiko Stuebner Acked-by: Stephen Boyd Signed-off-by: Ulf Hansson diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index 9b61342..a797d86 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -45,8 +45,8 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw, #define PSECS_PER_SEC 1000000000000LL /* - * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to - * simplify calculations. So 45degs could be anywhere between 33deg and 66deg. + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg. */ #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 @@ -84,22 +84,37 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) u32 raw_value; u64 delay; - /* allow 22 to be 22.5 */ - degrees++; - /* floor to 22.5 increment */ - degrees -= ((degrees) * 10 % 225) / 10; - nineties = degrees / 90; - /* 22.5 multiples */ - remainder = (degrees % 90) / 22; - + remainder = (degrees % 90); + + /* + * Due to the inexact nature of the "fine" delay, we might + * actually go non-monotonic. We don't go _too_ monotonic + * though, so we should be OK. Here are options of how we may + * work: + * + * Ideally we end up with: + * 1.0, 2.0, ..., 69.0, 70.0, ..., 89.0, 90.0 + * + * On one extreme (if delay is actually 44ps): + * .73, 1.5, ..., 50.6, 51.3, ..., 65.3, 90.0 + * The other (if delay is actually 77ps): + * 1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90 + * + * It's possible we might make a delay that is up to 25 + * degrees off from what we think we're making. That's OK + * though because we should be REALLY far from any bad range. + */ + + /* + * Convert to delay; do a little extra work to make sure we + * don't overflow 32-bit / 64-bit numbers. + */ delay = PSECS_PER_SEC; - do_div(delay, rate); - /* / 360 / 22.5 */ - do_div(delay, 16); - do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC); - delay *= remainder; + do_div(delay, 10000); + do_div(delay, (rate / 1000) * 36 * ROCKCHIP_MMC_DELAY_ELEMENT_PSEC); + delay_num = (u8) min(delay, 255ULL); raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; -- cgit v0.10.2 From 4351f19a337d48a780762d076408ebe9b0c1f945 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 30 Sep 2015 16:07:38 +0200 Subject: clk: rockchip: Make calculations use rounding Let's use DIV_ROUND_CLOSEST for rounding, not just truncating division. This lets us get closer to the right rate. Before this: set_phase(86) delay_nums=26 reg[0xf000420c]=0x468 actual_degrees=83 set_phase(89) delay_nums=27 reg[0xf000420c]=0x46c actual_degrees=86 After this: set_phase(86) delay_nums=27 reg[0xf000420c]=0x46c actual_degrees=86 set_phase(89) delay_nums=28 reg[0xf000420c]=0x470 actual_degrees=90 Signed-off-by: Douglas Anderson Signed-off-by: Heiko Stuebner Acked-by: Stephen Boyd Signed-off-by: Ulf Hansson diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index a797d86..bc24e5a 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -69,7 +69,7 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw) delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; - degrees += delay_num * factor / 10000; + degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000); } return degrees % 360; @@ -82,7 +82,7 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) u8 nineties, remainder; u8 delay_num; u32 raw_value; - u64 delay; + u32 delay; nineties = degrees / 90; remainder = (degrees % 90); @@ -110,12 +110,13 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) * Convert to delay; do a little extra work to make sure we * don't overflow 32-bit / 64-bit numbers. */ - delay = PSECS_PER_SEC; + delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */ delay *= remainder; - do_div(delay, 10000); - do_div(delay, (rate / 1000) * 36 * ROCKCHIP_MMC_DELAY_ELEMENT_PSEC); + delay = DIV_ROUND_CLOSEST(delay, + (rate / 1000) * 36 * + (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10)); - delay_num = (u8) min(delay, 255ULL); + delay_num = (u8) min_t(u32, delay, 255); raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; -- cgit v0.10.2 From 06bf9c565444643ce2732f8a297f76b953acde6b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Oct 2015 10:26:21 +0300 Subject: mmc: sdhci-pci: Add another PCI ID for an Intel eMMC host controller Add another PCI ID for an Intel eMMC host controller. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 6d86dc8..b6f6117 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1109,6 +1109,14 @@ static const struct pci_device_id pci_ids[] = { }, { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_DNV_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, .subvendor = PCI_ANY_ID, diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 541f1ca..cd4f4d7 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -24,6 +24,7 @@ #define PCI_DEVICE_ID_INTEL_SPT_EMMC 0x9d2b #define PCI_DEVICE_ID_INTEL_SPT_SDIO 0x9d2c #define PCI_DEVICE_ID_INTEL_SPT_SD 0x9d2d +#define PCI_DEVICE_ID_INTEL_DNV_EMMC 0x19db /* * PCI registers -- cgit v0.10.2 From e38eeca5c23389f9a606e4847f0f9731b2e889f2 Mon Sep 17 00:00:00 2001 From: yangbo lu Date: Thu, 8 Oct 2015 18:36:23 +0800 Subject: mmc: dt: add DT binding for little-endian eSDHC host controller Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt index b7943f3..dedfb02 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt @@ -22,6 +22,8 @@ Optional properties: - voltage-ranges : two cells are required, first cell specifies minimum slot voltage (mV), second cell specifies maximum slot voltage (mV). Several ranges could be specified. + - little-endian : If the host controller is little-endian mode, specify + this property. The default endian mode is big-endian. Example: -- cgit v0.10.2 From f4932cfd22f151af19d552c0ee607b2fb9c41b53 Mon Sep 17 00:00:00 2001 From: yangbo lu Date: Thu, 8 Oct 2015 18:36:36 +0800 Subject: mmc: sdhci-of-esdhc: support both BE and LE host controller To support little endian eSDHC controller, we redefine both BE and LE IO accessors. In the new accessors, use ioread*/iowrite* instead of in_be32/out_be32 and will select accessors according to endian mode in probe function. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 163ac99..de132e2 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -24,6 +24,8 @@ SDHCI_QUIRK_PIO_NEEDS_DELAY | \ SDHCI_QUIRK_NO_HISPD_BIT) +#define ESDHC_PROCTL 0x28 + #define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_CLOCK_MASK 0x0000fff0 #define ESDHC_PREDIV_SHIFT 8 diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 2a54dbe..910838f 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -24,122 +24,318 @@ #define VENDOR_V_22 0x12 #define VENDOR_V_23 0x13 -static u32 esdhc_readl(struct sdhci_host *host, int reg) + +struct sdhci_esdhc { + u8 vendor_ver; + u8 spec_ver; +}; + +/** + * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register + * to make it compatible with SD spec. + * + * @host: pointer to sdhci_host + * @spec_reg: SD spec register address + * @value: 32bit eSDHC register value on spec_reg address + * + * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC + * registers are 32 bits. There are differences in register size, register + * address, register function, bit position and function between eSDHC spec + * and SD spec. + * + * Return a fixed up register value + */ +static u32 esdhc_readl_fixup(struct sdhci_host *host, + int spec_reg, u32 value) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = pltfm_host->priv; u32 ret; - ret = in_be32(host->ioaddr + reg); /* * The bit of ADMA flag in eSDHC is not compatible with standard * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is * supported by eSDHC. * And for many FSL eSDHC controller, the reset value of field - * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA, + * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA, * only these vendor version is greater than 2.2/0x12 support ADMA. - * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the - * the verdor version number, oxFE is SDHCI_HOST_VERSION. */ - if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { - u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); - tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; - if (tmp > VENDOR_V_22) - ret |= SDHCI_CAN_DO_ADMA2; + if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) { + if (esdhc->vendor_ver > VENDOR_V_22) { + ret = value | SDHCI_CAN_DO_ADMA2; + return ret; + } } - + ret = value; return ret; } -static u16 esdhc_readw(struct sdhci_host *host, int reg) +static u16 esdhc_readw_fixup(struct sdhci_host *host, + int spec_reg, u32 value) { u16 ret; - int base = reg & ~0x3; - int shift = (reg & 0x2) * 8; + int shift = (spec_reg & 0x2) * 8; - if (unlikely(reg == SDHCI_HOST_VERSION)) - ret = in_be32(host->ioaddr + base) & 0xffff; + if (spec_reg == SDHCI_HOST_VERSION) + ret = value & 0xffff; else - ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; + ret = (value >> shift) & 0xffff; return ret; } -static u8 esdhc_readb(struct sdhci_host *host, int reg) +static u8 esdhc_readb_fixup(struct sdhci_host *host, + int spec_reg, u32 value) { - int base = reg & ~0x3; - int shift = (reg & 0x3) * 8; - u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; + u8 ret; + u8 dma_bits; + int shift = (spec_reg & 0x3) * 8; + + ret = (value >> shift) & 0xff; /* * "DMA select" locates at offset 0x28 in SD specification, but on * P5020 or P3041, it locates at 0x29. */ - if (reg == SDHCI_HOST_CONTROL) { - u32 dma_bits; - - dma_bits = in_be32(host->ioaddr + reg); + if (spec_reg == SDHCI_HOST_CONTROL) { /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; - + dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK; /* fixup the result */ ret &= ~SDHCI_CTRL_DMA_MASK; ret |= dma_bits; } - return ret; } -static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) +/** + * esdhc_write*_fixup - Fixup the SD spec register value so that it could be + * written into eSDHC register. + * + * @host: pointer to sdhci_host + * @spec_reg: SD spec register address + * @value: 8/16/32bit SD spec register value that would be written + * @old_value: 32bit eSDHC register value on spec_reg address + * + * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC + * registers are 32 bits. There are differences in register size, register + * address, register function, bit position and function between eSDHC spec + * and SD spec. + * + * Return a fixed up register value + */ +static u32 esdhc_writel_fixup(struct sdhci_host *host, + int spec_reg, u32 value, u32 old_value) { + u32 ret; + /* - * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] - * when SYSCTL[RSTD]) is set for some special operations. - * No any impact other operation. + * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] + * when SYSCTL[RSTD] is set for some special operations. + * No any impact on other operation. */ - if (reg == SDHCI_INT_ENABLE) - val |= SDHCI_INT_BLK_GAP; - sdhci_be32bs_writel(host, val, reg); + if (spec_reg == SDHCI_INT_ENABLE) + ret = value | SDHCI_INT_BLK_GAP; + else + ret = value; + + return ret; } -static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) +static u32 esdhc_writew_fixup(struct sdhci_host *host, + int spec_reg, u16 value, u32 old_value) { - if (reg == SDHCI_BLOCK_SIZE) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + int shift = (spec_reg & 0x2) * 8; + u32 ret; + + switch (spec_reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. Return old value. + */ + pltfm_host->xfer_mode_shadow = value; + return old_value; + case SDHCI_COMMAND: + ret = (value << 16) | pltfm_host->xfer_mode_shadow; + return ret; + } + + ret = old_value & (~(0xffff << shift)); + ret |= (value << shift); + + if (spec_reg == SDHCI_BLOCK_SIZE) { /* * Two last DMA bits are reserved, and first one is used for * non-standard blksz of 4096 bytes that we don't support * yet. So clear the DMA boundary bits. */ - val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); + ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0)); } - sdhci_be32bs_writew(host, val, reg); + return ret; } -static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) +static u32 esdhc_writeb_fixup(struct sdhci_host *host, + int spec_reg, u8 value, u32 old_value) { + u32 ret; + u32 dma_bits; + u8 tmp; + int shift = (spec_reg & 0x3) * 8; + /* * "DMA select" location is offset 0x28 in SD specification, but on * P5020 or P3041, it's located at 0x29. */ - if (reg == SDHCI_HOST_CONTROL) { - u32 dma_bits; - + if (spec_reg == SDHCI_HOST_CONTROL) { /* * If host control register is not standard, exit * this function */ if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) - return; + return old_value; /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; - clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, - dma_bits); - val &= ~SDHCI_CTRL_DMA_MASK; - val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; + dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5; + ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits; + tmp = (value & (~SDHCI_CTRL_DMA_MASK)) | + (old_value & SDHCI_CTRL_DMA_MASK); + ret = (ret & (~0xff)) | tmp; + + /* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */ + ret &= ~ESDHC_HOST_CONTROL_RES; + return ret; } - /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ - if (reg == SDHCI_HOST_CONTROL) - val &= ~ESDHC_HOST_CONTROL_RES; - sdhci_be32bs_writeb(host, val, reg); + ret = (old_value & (~(0xff << shift))) | (value << shift); + return ret; +} + +static u32 esdhc_be_readl(struct sdhci_host *host, int reg) +{ + u32 ret; + u32 value; + + value = ioread32be(host->ioaddr + reg); + ret = esdhc_readl_fixup(host, reg, value); + + return ret; +} + +static u32 esdhc_le_readl(struct sdhci_host *host, int reg) +{ + u32 ret; + u32 value; + + value = ioread32(host->ioaddr + reg); + ret = esdhc_readl_fixup(host, reg, value); + + return ret; +} + +static u16 esdhc_be_readw(struct sdhci_host *host, int reg) +{ + u16 ret; + u32 value; + int base = reg & ~0x3; + + value = ioread32be(host->ioaddr + base); + ret = esdhc_readw_fixup(host, reg, value); + return ret; +} + +static u16 esdhc_le_readw(struct sdhci_host *host, int reg) +{ + u16 ret; + u32 value; + int base = reg & ~0x3; + + value = ioread32(host->ioaddr + base); + ret = esdhc_readw_fixup(host, reg, value); + return ret; +} + +static u8 esdhc_be_readb(struct sdhci_host *host, int reg) +{ + u8 ret; + u32 value; + int base = reg & ~0x3; + + value = ioread32be(host->ioaddr + base); + ret = esdhc_readb_fixup(host, reg, value); + return ret; +} + +static u8 esdhc_le_readb(struct sdhci_host *host, int reg) +{ + u8 ret; + u32 value; + int base = reg & ~0x3; + + value = ioread32(host->ioaddr + base); + ret = esdhc_readb_fixup(host, reg, value); + return ret; +} + +static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg) +{ + u32 value; + + value = esdhc_writel_fixup(host, reg, val, 0); + iowrite32be(value, host->ioaddr + reg); +} + +static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg) +{ + u32 value; + + value = esdhc_writel_fixup(host, reg, val, 0); + iowrite32(value, host->ioaddr + reg); +} + +static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg) +{ + int base = reg & ~0x3; + u32 value; + u32 ret; + + value = ioread32be(host->ioaddr + base); + ret = esdhc_writew_fixup(host, reg, val, value); + if (reg != SDHCI_TRANSFER_MODE) + iowrite32be(ret, host->ioaddr + base); +} + +static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg) +{ + int base = reg & ~0x3; + u32 value; + u32 ret; + + value = ioread32(host->ioaddr + base); + ret = esdhc_writew_fixup(host, reg, val, value); + if (reg != SDHCI_TRANSFER_MODE) + iowrite32(ret, host->ioaddr + base); +} + +static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg) +{ + int base = reg & ~0x3; + u32 value; + u32 ret; + + value = ioread32be(host->ioaddr + base); + ret = esdhc_writeb_fixup(host, reg, val, value); + iowrite32be(ret, host->ioaddr + base); +} + +static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg) +{ + int base = reg & ~0x3; + u32 value; + u32 ret; + + value = ioread32(host->ioaddr + base); + ret = esdhc_writeb_fixup(host, reg, val, value); + iowrite32(ret, host->ioaddr + base); } /* @@ -149,19 +345,17 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) * For Continue, apply soft reset for data(SYSCTL[RSTD]); * and re-issue the entire read transaction from beginning. */ -static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) +static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask) { - u32 tmp; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = pltfm_host->priv; bool applicable; dma_addr_t dmastart; dma_addr_t dmanow; - tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); - tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; - applicable = (intmask & SDHCI_INT_DATA_END) && - (intmask & SDHCI_INT_BLK_GAP) && - (tmp == VENDOR_V_23); + (intmask & SDHCI_INT_BLK_GAP) && + (esdhc->vendor_ver == VENDOR_V_23); if (!applicable) return; @@ -179,7 +373,11 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) static int esdhc_of_enable_dma(struct sdhci_host *host) { - setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); + u32 value; + + value = sdhci_readl(host, ESDHC_DMA_SYSCTL); + value |= ESDHC_DMA_SNOOP; + sdhci_writel(host, value, ESDHC_DMA_SYSCTL); return 0; } @@ -199,6 +397,8 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = pltfm_host->priv; int pre_div = 1; int div = 1; u32 temp; @@ -209,9 +409,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) return; /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ - temp = esdhc_readw(host, SDHCI_HOST_VERSION); - temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; - if (temp < VENDOR_V_23) + if (esdhc->vendor_ver < VENDOR_V_23) pre_div = 2; /* Workaround to reduce the clock frequency for p1010 esdhc */ @@ -247,39 +445,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) mdelay(1); } -static void esdhc_of_platform_init(struct sdhci_host *host) -{ - u32 vvn; - - vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); - vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; - if (vvn == VENDOR_V_22) - host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; - - if (vvn > VENDOR_V_22) - host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; -} - static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) { u32 ctrl; + ctrl = sdhci_readl(host, ESDHC_PROCTL); + ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK); switch (width) { case MMC_BUS_WIDTH_8: - ctrl = ESDHC_CTRL_8BITBUS; + ctrl |= ESDHC_CTRL_8BITBUS; break; case MMC_BUS_WIDTH_4: - ctrl = ESDHC_CTRL_4BITBUS; + ctrl |= ESDHC_CTRL_4BITBUS; break; default: - ctrl = 0; break; } - clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, - ESDHC_CTRL_BUSWIDTH_MASK, ctrl); + sdhci_writel(host, ctrl, ESDHC_PROCTL); } static void esdhc_reset(struct sdhci_host *host, u8 mask) @@ -290,32 +475,13 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } -static const struct sdhci_ops sdhci_esdhc_ops = { - .read_l = esdhc_readl, - .read_w = esdhc_readw, - .read_b = esdhc_readb, - .write_l = esdhc_writel, - .write_w = esdhc_writew, - .write_b = esdhc_writeb, - .set_clock = esdhc_of_set_clock, - .enable_dma = esdhc_of_enable_dma, - .get_max_clock = esdhc_of_get_max_clock, - .get_min_clock = esdhc_of_get_min_clock, - .platform_init = esdhc_of_platform_init, - .adma_workaround = esdhci_of_adma_workaround, - .set_bus_width = esdhc_pltfm_set_bus_width, - .reset = esdhc_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, -}; - #ifdef CONFIG_PM - static u32 esdhc_proctl; static int esdhc_of_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); - esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL); return sdhci_suspend_host(host); } @@ -328,9 +494,8 @@ static int esdhc_of_resume(struct device *dev) if (ret == 0) { /* Isn't this already done by sdhci_resume_host() ? --rmk */ esdhc_of_enable_dma(host); - sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); + sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); } - return ret; } @@ -343,30 +508,92 @@ static const struct dev_pm_ops esdhc_pmops = { #define ESDHC_PMOPS NULL #endif -static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { - /* - * card detection could be handled via GPIO - * eSDHC cannot support End Attribute in NOP ADMA descriptor - */ +static const struct sdhci_ops sdhci_esdhc_be_ops = { + .read_l = esdhc_be_readl, + .read_w = esdhc_be_readw, + .read_b = esdhc_be_readb, + .write_l = esdhc_be_writel, + .write_w = esdhc_be_writew, + .write_b = esdhc_be_writeb, + .set_clock = esdhc_of_set_clock, + .enable_dma = esdhc_of_enable_dma, + .get_max_clock = esdhc_of_get_max_clock, + .get_min_clock = esdhc_of_get_min_clock, + .adma_workaround = esdhc_of_adma_workaround, + .set_bus_width = esdhc_pltfm_set_bus_width, + .reset = esdhc_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static const struct sdhci_ops sdhci_esdhc_le_ops = { + .read_l = esdhc_le_readl, + .read_w = esdhc_le_readw, + .read_b = esdhc_le_readb, + .write_l = esdhc_le_writel, + .write_w = esdhc_le_writew, + .write_b = esdhc_le_writeb, + .set_clock = esdhc_of_set_clock, + .enable_dma = esdhc_of_enable_dma, + .get_max_clock = esdhc_of_get_max_clock, + .get_min_clock = esdhc_of_get_min_clock, + .adma_workaround = esdhc_of_adma_workaround, + .set_bus_width = esdhc_pltfm_set_bus_width, + .reset = esdhc_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { + .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION + | SDHCI_QUIRK_NO_CARD_NO_RESET + | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .ops = &sdhci_esdhc_be_ops, +}; + +static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = { .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_NO_CARD_NO_RESET | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .ops = &sdhci_esdhc_ops, + .ops = &sdhci_esdhc_le_ops, }; +static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_esdhc *esdhc; + u16 host_ver; + + pltfm_host = sdhci_priv(host); + esdhc = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_esdhc), + GFP_KERNEL); + + host_ver = sdhci_readw(host, SDHCI_HOST_VERSION); + esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >> + SDHCI_VENDOR_VER_SHIFT; + esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK; + + pltfm_host->priv = esdhc; +} + static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; struct device_node *np; int ret; - host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); + np = pdev->dev.of_node; + + if (of_get_property(np, "little-endian", NULL)) + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, 0); + else + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata, 0); + if (IS_ERR(host)) return PTR_ERR(host); + esdhc_init(pdev, host); + sdhci_get_of_property(pdev); - np = pdev->dev.of_node; if (of_device_is_compatible(np, "fsl,p5040-esdhc") || of_device_is_compatible(np, "fsl,p5020-esdhc") || of_device_is_compatible(np, "fsl,p4080-esdhc") || -- cgit v0.10.2 From 2ca635242ec40c8d575e337a0b2b0a1928ad6581 Mon Sep 17 00:00:00 2001 From: yangbo lu Date: Thu, 8 Oct 2015 18:36:47 +0800 Subject: mmc: kconfig: reconfigure MMC_SDHCI_OF_ESDHC option Freescale eSDHC driver now supports both little-endian and big-endian mode eSDHC IPs for ARM and PPC. So, MMC_SDHCI_OF_ESDHC option needs to be reconfigured. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 8a1e349..12b274a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -67,7 +67,7 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER has the effect of scrambling the addresses and formats of data accessed in sizes other than the datum size. - This is the case for the Freescale eSDHC and Nintendo Wii SDHCI. + This is the case for the Nintendo Wii SDHCI. config MMC_SDHCI_PCI tristate "SDHCI support on PCI bus" @@ -140,8 +140,8 @@ config MMC_SDHCI_OF_AT91 config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM - depends on PPC - select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER + depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE + select MMC_SDHCI_IO_ACCESSORS help This selects the Freescale eSDHC controller support. -- cgit v0.10.2 From a22950c888e3770c958a5c489906b48d3764a783 Mon Sep 17 00:00:00 2001 From: yangbo lu Date: Thu, 8 Oct 2015 18:36:57 +0800 Subject: mmc: sdhci-of-esdhc: add quirk SDHCI_QUIRK_BROKEN_TIMEOUT_VAL for ls1021a Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 910838f..7b9282b 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -602,6 +602,9 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) of_device_is_compatible(np, "fsl,ls1021a-esdhc")) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard -- cgit v0.10.2 From 9eadcc0581a8ccaf4c2378aa1c193fb164304f1d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 2 Oct 2015 10:56:11 +0200 Subject: mmc: core: Remove MMC_CLKGATE MMC_CLKGATE was once invented to save power by gating the bus clock at request inactivity. At that time it served its purpose. The modern way to deal with power saving for these scenarios, is by using runtime PM. Nowadays, several host drivers have deployed runtime PM, but for those that haven't and which still cares power saving at request inactivity, it's certainly time to deploy runtime PM as it has been around for several years now. To simplify code to mmc core and thus decrease maintenance efforts, this patch removes all code related to MMC_CLKGATE. Signed-off-by: Ulf Hansson Reviewed-by: Linus Walleij diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt index 189bab0..caa5557 100644 --- a/Documentation/mmc/mmc-dev-attrs.txt +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -72,13 +72,3 @@ Note on raw_rpmb_size_mult: "raw_rpmb_size_mult" is a mutliple of 128kB block. RPMB size in byte is calculated by using the following equation: RPMB partition size = 128kB x raw_rpmb_size_mult - -SD/MMC/SDIO Clock Gating Attribute -================================== - -Read and write access is provided to following attribute. -This attribute appears only if CONFIG_MMC_CLKGATE is enabled. - - clkgate_delay Tune the clock gating delay with desired value in milliseconds. - -echo > /sys/class/mmc_host/mmcX/clkgate_delay diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 9ebee72..4c33d76 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -1,13 +1,3 @@ # # MMC core configuration # - -config MMC_CLKGATE - bool "MMC host clock gating" - help - This will attempt to aggressively gate the clock to the MMC card. - This is done to save power due to gating off the logic and bus - noise when the MMC card is not in use. Your host driver has to - support handling this in order for it to be of any use. - - If unsure, say N. diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 21cda23..1a36b02 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -187,8 +187,6 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) if (mrq->done) mrq->done(mrq); - - mmc_host_clk_release(host); } } @@ -292,7 +290,6 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mrq->stop->mrq = mrq; } } - mmc_host_clk_hold(host); led_trigger_event(host->led, LED_FULL); __mmc_start_request(host, mrq); @@ -542,11 +539,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host, static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, bool is_first_req) { - if (host->ops->pre_req) { - mmc_host_clk_hold(host); + if (host->ops->pre_req) host->ops->pre_req(host, mrq, is_first_req); - mmc_host_clk_release(host); - } } /** @@ -561,11 +555,8 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err) { - if (host->ops->post_req) { - mmc_host_clk_hold(host); + if (host->ops->post_req) host->ops->post_req(host, mrq, err); - mmc_host_clk_release(host); - } } /** @@ -850,9 +841,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) unsigned int timeout_us, limit_us; timeout_us = data->timeout_ns / 1000; - if (mmc_host_clk_rate(card->host)) + if (card->host->ios.clock) timeout_us += data->timeout_clks * 1000 / - (mmc_host_clk_rate(card->host) / 1000); + (card->host->ios.clock / 1000); if (data->flags & MMC_DATA_WRITE) /* @@ -1050,8 +1041,6 @@ static inline void mmc_set_ios(struct mmc_host *host) ios->power_mode, ios->chip_select, ios->vdd, ios->bus_width, ios->timing); - if (ios->clock > 0) - mmc_set_ungated(host); host->ops->set_ios(host, ios); } @@ -1060,17 +1049,15 @@ static inline void mmc_set_ios(struct mmc_host *host) */ void mmc_set_chip_select(struct mmc_host *host, int mode) { - mmc_host_clk_hold(host); host->ios.chip_select = mode; mmc_set_ios(host); - mmc_host_clk_release(host); } /* * Sets the host clock to the highest possible frequency that * is below "hz". */ -static void __mmc_set_clock(struct mmc_host *host, unsigned int hz) +void mmc_set_clock(struct mmc_host *host, unsigned int hz) { WARN_ON(hz && hz < host->f_min); @@ -1081,68 +1068,6 @@ static void __mmc_set_clock(struct mmc_host *host, unsigned int hz) mmc_set_ios(host); } -void mmc_set_clock(struct mmc_host *host, unsigned int hz) -{ - mmc_host_clk_hold(host); - __mmc_set_clock(host, hz); - mmc_host_clk_release(host); -} - -#ifdef CONFIG_MMC_CLKGATE -/* - * This gates the clock by setting it to 0 Hz. - */ -void mmc_gate_clock(struct mmc_host *host) -{ - unsigned long flags; - - spin_lock_irqsave(&host->clk_lock, flags); - host->clk_old = host->ios.clock; - host->ios.clock = 0; - host->clk_gated = true; - spin_unlock_irqrestore(&host->clk_lock, flags); - mmc_set_ios(host); -} - -/* - * This restores the clock from gating by using the cached - * clock value. - */ -void mmc_ungate_clock(struct mmc_host *host) -{ - /* - * We should previously have gated the clock, so the clock shall - * be 0 here! The clock may however be 0 during initialization, - * when some request operations are performed before setting - * the frequency. When ungate is requested in that situation - * we just ignore the call. - */ - if (host->clk_old) { - BUG_ON(host->ios.clock); - /* This call will also set host->clk_gated to false */ - __mmc_set_clock(host, host->clk_old); - } -} - -void mmc_set_ungated(struct mmc_host *host) -{ - unsigned long flags; - - /* - * We've been given a new frequency while the clock is gated, - * so make sure we regard this as ungating it. - */ - spin_lock_irqsave(&host->clk_lock, flags); - host->clk_gated = false; - spin_unlock_irqrestore(&host->clk_lock, flags); -} - -#else -void mmc_set_ungated(struct mmc_host *host) -{ -} -#endif - int mmc_execute_tuning(struct mmc_card *card) { struct mmc_host *host = card->host; @@ -1157,9 +1082,7 @@ int mmc_execute_tuning(struct mmc_card *card) else opcode = MMC_SEND_TUNING_BLOCK; - mmc_host_clk_hold(host); err = host->ops->execute_tuning(host, opcode); - mmc_host_clk_release(host); if (err) pr_err("%s: tuning execution failed\n", mmc_hostname(host)); @@ -1174,10 +1097,8 @@ int mmc_execute_tuning(struct mmc_card *card) */ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) { - mmc_host_clk_hold(host); host->ios.bus_mode = mode; mmc_set_ios(host); - mmc_host_clk_release(host); } /* @@ -1185,10 +1106,8 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) */ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) { - mmc_host_clk_hold(host); host->ios.bus_width = width; mmc_set_ios(host); - mmc_host_clk_release(host); } /* @@ -1532,11 +1451,8 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) int old_signal_voltage = host->ios.signal_voltage; host->ios.signal_voltage = signal_voltage; - if (host->ops->start_signal_voltage_switch) { - mmc_host_clk_hold(host); + if (host->ops->start_signal_voltage_switch) err = host->ops->start_signal_voltage_switch(host, &host->ios); - mmc_host_clk_release(host); - } if (err) host->ios.signal_voltage = old_signal_voltage; @@ -1570,20 +1486,17 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) pr_warn("%s: cannot verify signal voltage switch\n", mmc_hostname(host)); - mmc_host_clk_hold(host); - cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(host, &cmd, 0); if (err) - goto err_command; + return err; + + if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) + return -EIO; - if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) { - err = -EIO; - goto err_command; - } /* * The card should drive cmd and dat[0:3] low immediately * after the response of cmd11, but wait 1 ms to be sure @@ -1632,9 +1545,6 @@ power_cycle: mmc_power_cycle(host, ocr); } -err_command: - mmc_host_clk_release(host); - return err; } @@ -1643,10 +1553,8 @@ err_command: */ void mmc_set_timing(struct mmc_host *host, unsigned int timing) { - mmc_host_clk_hold(host); host->ios.timing = timing; mmc_set_ios(host); - mmc_host_clk_release(host); } /* @@ -1654,10 +1562,8 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) */ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) { - mmc_host_clk_hold(host); host->ios.drv_type = drv_type; mmc_set_ios(host); - mmc_host_clk_release(host); } int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, @@ -1665,7 +1571,6 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, { struct mmc_host *host = card->host; int host_drv_type = SD_DRIVER_TYPE_B; - int drive_strength; *drv_type = 0; @@ -1688,14 +1593,10 @@ int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, * information and let the hardware specific code * return what is possible given the options */ - mmc_host_clk_hold(host); - drive_strength = host->ops->select_drive_strength(card, max_dtr, - host_drv_type, - card_drv_type, - drv_type); - mmc_host_clk_release(host); - - return drive_strength; + return host->ops->select_drive_strength(card, max_dtr, + host_drv_type, + card_drv_type, + drv_type); } /* @@ -1714,8 +1615,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) if (host->ios.power_mode == MMC_POWER_ON) return; - mmc_host_clk_hold(host); - mmc_pwrseq_pre_power_on(host); host->ios.vdd = fls(ocr) - 1; @@ -1749,8 +1648,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) * time required to reach a stable voltage. */ mmc_delay(10); - - mmc_host_clk_release(host); } void mmc_power_off(struct mmc_host *host) @@ -1758,8 +1655,6 @@ void mmc_power_off(struct mmc_host *host) if (host->ios.power_mode == MMC_POWER_OFF) return; - mmc_host_clk_hold(host); - mmc_pwrseq_power_off(host); host->ios.clock = 0; @@ -1775,8 +1670,6 @@ void mmc_power_off(struct mmc_host *host) * can be successfully turned on again. */ mmc_delay(1); - - mmc_host_clk_release(host); } void mmc_power_cycle(struct mmc_host *host, u32 ocr) @@ -1992,7 +1885,7 @@ static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card, */ timeout_clks <<= 1; timeout_us += (timeout_clks * 1000) / - (mmc_host_clk_rate(card->host) / 1000); + (card->host->ios.clock / 1000); erase_timeout = timeout_us / 1000; @@ -2440,9 +2333,7 @@ static void mmc_hw_reset_for_init(struct mmc_host *host) { if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) return; - mmc_host_clk_hold(host); host->ops->hw_reset(host); - mmc_host_clk_release(host); } int mmc_hw_reset(struct mmc_host *host) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 1a22a82..09241e5 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -40,9 +40,6 @@ void mmc_init_erase(struct mmc_card *card); void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); -void mmc_gate_clock(struct mmc_host *host); -void mmc_ungate_clock(struct mmc_host *host); -void mmc_set_ungated(struct mmc_host *host); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index f4db93e..154aced 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -255,11 +255,6 @@ void mmc_add_host_debugfs(struct mmc_host *host) &mmc_clock_fops)) goto err_node; -#ifdef CONFIG_MMC_CLKGATE - if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), - root, &host->clk_delay)) - goto err_node; -#endif #ifdef CONFIG_FAIL_MMC_REQUEST if (fail_request) setup_fault_attr(&fail_default_attr, fail_request); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 5466f25..970e690 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -61,246 +61,6 @@ void mmc_unregister_host_class(void) class_unregister(&mmc_host_class); } -#ifdef CONFIG_MMC_CLKGATE -static ssize_t clkgate_delay_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mmc_host *host = cls_dev_to_mmc_host(dev); - return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay); -} - -static ssize_t clkgate_delay_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct mmc_host *host = cls_dev_to_mmc_host(dev); - unsigned long flags, value; - - if (kstrtoul(buf, 0, &value)) - return -EINVAL; - - spin_lock_irqsave(&host->clk_lock, flags); - host->clkgate_delay = value; - spin_unlock_irqrestore(&host->clk_lock, flags); - return count; -} - -/* - * Enabling clock gating will make the core call out to the host - * once up and once down when it performs a request or card operation - * intermingled in any fashion. The driver will see this through - * set_ios() operations with ios.clock field set to 0 to gate (disable) - * the block clock, and to the old frequency to enable it again. - */ -static void mmc_host_clk_gate_delayed(struct mmc_host *host) -{ - unsigned long tick_ns; - unsigned long freq = host->ios.clock; - unsigned long flags; - - if (!freq) { - pr_debug("%s: frequency set to 0 in disable function, " - "this means the clock is already disabled.\n", - mmc_hostname(host)); - return; - } - /* - * New requests may have appeared while we were scheduling, - * then there is no reason to delay the check before - * clk_disable(). - */ - spin_lock_irqsave(&host->clk_lock, flags); - - /* - * Delay n bus cycles (at least 8 from MMC spec) before attempting - * to disable the MCI block clock. The reference count may have - * gone up again after this delay due to rescheduling! - */ - if (!host->clk_requests) { - spin_unlock_irqrestore(&host->clk_lock, flags); - tick_ns = DIV_ROUND_UP(1000000000, freq); - ndelay(host->clk_delay * tick_ns); - } else { - /* New users appeared while waiting for this work */ - spin_unlock_irqrestore(&host->clk_lock, flags); - return; - } - mutex_lock(&host->clk_gate_mutex); - spin_lock_irqsave(&host->clk_lock, flags); - if (!host->clk_requests) { - spin_unlock_irqrestore(&host->clk_lock, flags); - /* This will set host->ios.clock to 0 */ - mmc_gate_clock(host); - spin_lock_irqsave(&host->clk_lock, flags); - pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); - } - spin_unlock_irqrestore(&host->clk_lock, flags); - mutex_unlock(&host->clk_gate_mutex); -} - -/* - * Internal work. Work to disable the clock at some later point. - */ -static void mmc_host_clk_gate_work(struct work_struct *work) -{ - struct mmc_host *host = container_of(work, struct mmc_host, - clk_gate_work.work); - - mmc_host_clk_gate_delayed(host); -} - -/** - * mmc_host_clk_hold - ungate hardware MCI clocks - * @host: host to ungate. - * - * Makes sure the host ios.clock is restored to a non-zero value - * past this call. Increase clock reference count and ungate clock - * if we're the first user. - */ -void mmc_host_clk_hold(struct mmc_host *host) -{ - unsigned long flags; - - /* cancel any clock gating work scheduled by mmc_host_clk_release() */ - cancel_delayed_work_sync(&host->clk_gate_work); - mutex_lock(&host->clk_gate_mutex); - spin_lock_irqsave(&host->clk_lock, flags); - if (host->clk_gated) { - spin_unlock_irqrestore(&host->clk_lock, flags); - mmc_ungate_clock(host); - spin_lock_irqsave(&host->clk_lock, flags); - pr_debug("%s: ungated MCI clock\n", mmc_hostname(host)); - } - host->clk_requests++; - spin_unlock_irqrestore(&host->clk_lock, flags); - mutex_unlock(&host->clk_gate_mutex); -} - -/** - * mmc_host_may_gate_card - check if this card may be gated - * @card: card to check. - */ -static bool mmc_host_may_gate_card(struct mmc_card *card) -{ - /* If there is no card we may gate it */ - if (!card) - return true; - /* - * Don't gate SDIO cards! These need to be clocked at all times - * since they may be independent systems generating interrupts - * and other events. The clock requests counter from the core will - * go down to zero since the core does not need it, but we will not - * gate the clock, because there is somebody out there that may still - * be using it. - */ - return !(card->quirks & MMC_QUIRK_BROKEN_CLK_GATING); -} - -/** - * mmc_host_clk_release - gate off hardware MCI clocks - * @host: host to gate. - * - * Calls the host driver with ios.clock set to zero as often as possible - * in order to gate off hardware MCI clocks. Decrease clock reference - * count and schedule disabling of clock. - */ -void mmc_host_clk_release(struct mmc_host *host) -{ - unsigned long flags; - - spin_lock_irqsave(&host->clk_lock, flags); - host->clk_requests--; - if (mmc_host_may_gate_card(host->card) && - !host->clk_requests) - schedule_delayed_work(&host->clk_gate_work, - msecs_to_jiffies(host->clkgate_delay)); - spin_unlock_irqrestore(&host->clk_lock, flags); -} - -/** - * mmc_host_clk_rate - get current clock frequency setting - * @host: host to get the clock frequency for. - * - * Returns current clock frequency regardless of gating. - */ -unsigned int mmc_host_clk_rate(struct mmc_host *host) -{ - unsigned long freq; - unsigned long flags; - - spin_lock_irqsave(&host->clk_lock, flags); - if (host->clk_gated) - freq = host->clk_old; - else - freq = host->ios.clock; - spin_unlock_irqrestore(&host->clk_lock, flags); - return freq; -} - -/** - * mmc_host_clk_init - set up clock gating code - * @host: host with potential clock to control - */ -static inline void mmc_host_clk_init(struct mmc_host *host) -{ - host->clk_requests = 0; - /* Hold MCI clock for 8 cycles by default */ - host->clk_delay = 8; - /* - * Default clock gating delay is 0ms to avoid wasting power. - * This value can be tuned by writing into sysfs entry. - */ - host->clkgate_delay = 0; - host->clk_gated = false; - INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); - spin_lock_init(&host->clk_lock); - mutex_init(&host->clk_gate_mutex); -} - -/** - * mmc_host_clk_exit - shut down clock gating code - * @host: host with potential clock to control - */ -static inline void mmc_host_clk_exit(struct mmc_host *host) -{ - /* - * Wait for any outstanding gate and then make sure we're - * ungated before exiting. - */ - if (cancel_delayed_work_sync(&host->clk_gate_work)) - mmc_host_clk_gate_delayed(host); - if (host->clk_gated) - mmc_host_clk_hold(host); - /* There should be only one user now */ - WARN_ON(host->clk_requests > 1); -} - -static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) -{ - host->clkgate_delay_attr.show = clkgate_delay_show; - host->clkgate_delay_attr.store = clkgate_delay_store; - sysfs_attr_init(&host->clkgate_delay_attr.attr); - host->clkgate_delay_attr.attr.name = "clkgate_delay"; - host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR; - if (device_create_file(&host->class_dev, &host->clkgate_delay_attr)) - pr_err("%s: Failed to create clkgate_delay sysfs entry\n", - mmc_hostname(host)); -} -#else - -static inline void mmc_host_clk_init(struct mmc_host *host) -{ -} - -static inline void mmc_host_clk_exit(struct mmc_host *host) -{ -} - -static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) -{ -} - -#endif - void mmc_retune_enable(struct mmc_host *host) { host->can_retune = 1; @@ -583,8 +343,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) return NULL; } - mmc_host_clk_init(host); - spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); @@ -633,7 +391,6 @@ int mmc_add_host(struct mmc_host *host) #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); #endif - mmc_host_clk_sysfs_init(host); mmc_start_host(host); register_pm_notifier(&host->pm_notify); @@ -663,8 +420,6 @@ void mmc_remove_host(struct mmc_host *host) device_del(&host->class_dev); led_trigger_unregister_simple(host->led); - - mmc_host_clk_exit(host); } EXPORT_SYMBOL(mmc_remove_host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f6cd995..479b84a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1931,14 +1931,12 @@ static int mmc_reset(struct mmc_host *host) if (!mmc_can_reset(card)) return -EOPNOTSUPP; - mmc_host_clk_hold(host); mmc_set_clock(host, host->f_init); host->ops->hw_reset(host); /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); - mmc_host_clk_release(host); return mmc_init_card(host, card->ocr, card); } diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index dd1d1e0..fad660b 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -35,25 +35,7 @@ #define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128 #endif -/* - * This hook just adds a quirk for all sdio devices - */ -static void add_quirk_for_sdio_devices(struct mmc_card *card, int data) -{ - if (mmc_card_sdio(card)) - card->quirks |= data; -} - static const struct mmc_fixup mmc_fixup_methods[] = { - /* by default sdio devices are considered CLK_GATING broken */ - /* good cards will be whitelisted as they are tested */ - SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID, - add_quirk_for_sdio_devices, - MMC_QUIRK_BROKEN_CLK_GATING), - - SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, - remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING), - SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, add_quirk, MMC_QUIRK_NONSTD_FUNC_IF), diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index e124db0..b1b9200 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -800,9 +800,7 @@ static int mmc_sd_get_ro(struct mmc_host *host) if (!host->ops->get_ro) return -1; - mmc_host_clk_hold(host); ro = host->ops->get_ro(host); - mmc_host_clk_release(host); return ro; } diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 95bc101..16d838e 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -956,13 +956,10 @@ static int mmc_sdio_resume(struct mmc_host *host) } if (!err && host->sdio_irqs) { - if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) wake_up_process(host->sdio_irq_thread); - } else if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); + else if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 1); - mmc_host_clk_release(host); - } } mmc_release_host(host); diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 09cc67d..91bbbfb 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -168,21 +168,15 @@ static int sdio_irq_thread(void *_host) } set_current_state(TASK_INTERRUPTIBLE); - if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); + if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 1); - mmc_host_clk_release(host); - } if (!kthread_should_stop()) schedule_timeout(period); set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); - if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); + if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 0); - mmc_host_clk_release(host); - } pr_debug("%s: IRQ thread exiting with code %d\n", mmc_hostname(host), ret); @@ -208,9 +202,7 @@ static int sdio_card_irq_get(struct mmc_card *card) return err; } } else if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); - mmc_host_clk_release(host); } } @@ -229,9 +221,7 @@ static int sdio_card_irq_put(struct mmc_card *card) atomic_set(&host->sdio_irq_thread_abort, 1); kthread_stop(host->sdio_irq_thread); } else if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); - mmc_host_clk_release(host); } } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index fdd0779..eb0151b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -269,7 +269,6 @@ struct mmc_card { /* for byte mode */ #define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */ /* (missing CIA registers) */ -#define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) /* clock gating the sdio bus will make card fail */ #define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */ #define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ #define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 83b81fd..cfb3c99 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -292,18 +292,6 @@ struct mmc_host { mmc_pm_flag_t pm_caps; /* supported pm features */ -#ifdef CONFIG_MMC_CLKGATE - int clk_requests; /* internal reference counter */ - unsigned int clk_delay; /* number of MCI clk hold cycles */ - bool clk_gated; /* clock gated */ - struct delayed_work clk_gate_work; /* delayed clock gate */ - unsigned int clk_old; /* old clock value cache */ - spinlock_t clk_lock; /* lock for clk fields */ - struct mutex clk_gate_mutex; /* mutex for clock gating */ - struct device_attribute clkgate_delay_attr; - unsigned long clkgate_delay; -#endif - /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned short max_segs; /* see blk_queue_max_segments */ @@ -479,26 +467,6 @@ static inline int mmc_host_packed_wr(struct mmc_host *host) return host->caps2 & MMC_CAP2_PACKED_WR; } -#ifdef CONFIG_MMC_CLKGATE -void mmc_host_clk_hold(struct mmc_host *host); -void mmc_host_clk_release(struct mmc_host *host); -unsigned int mmc_host_clk_rate(struct mmc_host *host); - -#else -static inline void mmc_host_clk_hold(struct mmc_host *host) -{ -} - -static inline void mmc_host_clk_release(struct mmc_host *host) -{ -} - -static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) -{ - return host->ios.clock; -} -#endif - static inline int mmc_card_hs(struct mmc_card *card) { return card->host->ios.timing == MMC_TIMING_SD_HS || -- cgit v0.10.2 From 310c805e7f133443cd57f880b73557a4a8f54b30 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 12 Oct 2015 18:00:54 +0200 Subject: mmc: core: move ocr-bit to voltage translation into separate function We will shortly need the calculation of an ocr-bit to the actual voltage in a second place too, so move it from mmc_regulator_set_ocr to a common function mmc_ocrbitnum_to_vdd to make that possible. Signed-off-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1a36b02..7aa3b30 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1277,6 +1277,40 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host, #ifdef CONFIG_REGULATOR /** + * mmc_ocrbitnum_to_vdd - Convert a OCR bit number to its voltage + * @vdd_bit: OCR bit number + * @min_uV: minimum voltage value (mV) + * @max_uV: maximum voltage value (mV) + * + * This function returns the voltage range according to the provided OCR + * bit number. If conversion is not possible a negative errno value returned. + */ +static int mmc_ocrbitnum_to_vdd(int vdd_bit, int *min_uV, int *max_uV) +{ + int tmp; + + if (!vdd_bit) + return -EINVAL; + + /* + * REVISIT mmc_vddrange_to_ocrmask() may have set some + * bits this regulator doesn't quite support ... don't + * be too picky, most cards and regulators are OK with + * a 0.1V range goof (it's a small error percentage). + */ + tmp = vdd_bit - ilog2(MMC_VDD_165_195); + if (tmp == 0) { + *min_uV = 1650 * 1000; + *max_uV = 1950 * 1000; + } else { + *min_uV = 1900 * 1000 + tmp * 100 * 1000; + *max_uV = *min_uV + 100 * 1000; + } + + return 0; +} + +/** * mmc_regulator_get_ocrmask - return mask of supported voltages * @supply: regulator to use * @@ -1339,22 +1373,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, int min_uV, max_uV; if (vdd_bit) { - int tmp; - - /* - * REVISIT mmc_vddrange_to_ocrmask() may have set some - * bits this regulator doesn't quite support ... don't - * be too picky, most cards and regulators are OK with - * a 0.1V range goof (it's a small error percentage). - */ - tmp = vdd_bit - ilog2(MMC_VDD_165_195); - if (tmp == 0) { - min_uV = 1650 * 1000; - max_uV = 1950 * 1000; - } else { - min_uV = 1900 * 1000 + tmp * 100 * 1000; - max_uV = min_uV + 100 * 1000; - } + mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV); result = regulator_set_voltage(supply, min_uV, max_uV); if (result == 0 && !mmc->regulator_enabled) { -- cgit v0.10.2 From 2086f801cb2a796279e817e68255654c4cfd3be3 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 12 Oct 2015 14:48:25 +0200 Subject: mmc: core: Add mmc_regulator_set_vqmmc() This adds logic to the MMC core to set VQMMC. This is expected to be called by MMC drivers like dw_mmc as part of (or instead of) their start_signal_voltage_switch() callback. A few notes: * When setting the signal voltage to 3.3V we do our best to make VQMMC and VMMC match. It's been reported that this makes some old cards happy since they were tested back in the day before UHS when VQMMC and VMMC were provided by the same regulator. A nice side effect of this is that we don't end up on the hairy edge of VQMMC (2.7V), which some EEs claim is a little too close to the minimum for comfort. This is done in two steps. At first we try to find a VQMMC within a 0.3V tolerance of VMMC and if this is not supported by the supplying regulator we try to find a suitable voltage within the whole 2.7V-3.6V area of the spec. * The two step approach is currently necessary, as the used regulator_set_voltage_triplet(min, target, max) uses a simple implementation that just tries two basic steps: regulator_set_voltage(target, max); regulator_set_voltage(min, target); So with only one step with 2.7-3.6V borders, if a suitable voltage is a bit below VMMC, we would directly get the lowest 2.7V which some boards (like Rockchips) don't like at all. * When setting the signal voltage to 1.8V or 1.2V we aim for that specific voltage instead of picking the lowest one in the range. * We very purposely don't print errors in mmc_regulator_set_vqmmc(). There are cases where the MMC core will try several different voltages and we don't want to pollute the logs. Signed-off-by: Douglas Anderson Signed-off-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7aa3b30..5ae89e4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1394,6 +1394,84 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, } EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr); +static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator, + int min_uV, int target_uV, + int max_uV) +{ + /* + * Check if supported first to avoid errors since we may try several + * signal levels during power up and don't want to show errors. + */ + if (!regulator_is_supported_voltage(regulator, min_uV, max_uV)) + return -EINVAL; + + return regulator_set_voltage_triplet(regulator, min_uV, target_uV, + max_uV); +} + +/** + * mmc_regulator_set_vqmmc - Set VQMMC as per the ios + * + * For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible. + * That will match the behavior of old boards where VQMMC and VMMC were supplied + * by the same supply. The Bus Operating conditions for 3.3V signaling in the + * SD card spec also define VQMMC in terms of VMMC. + * If this is not possible we'll try the full 2.7-3.6V of the spec. + * + * For 1.2V and 1.8V signaling we'll try to get as close as possible to the + * requested voltage. This is definitely a good idea for UHS where there's a + * separate regulator on the card that's trying to make 1.8V and it's best if + * we match. + * + * This function is expected to be used by a controller's + * start_signal_voltage_switch() function. + */ +int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct device *dev = mmc_dev(mmc); + int ret, volt, min_uV, max_uV; + + /* If no vqmmc supply then we can't change the voltage */ + if (IS_ERR(mmc->supply.vqmmc)) + return -EINVAL; + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_120: + return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc, + 1100000, 1200000, 1300000); + case MMC_SIGNAL_VOLTAGE_180: + return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc, + 1700000, 1800000, 1950000); + case MMC_SIGNAL_VOLTAGE_330: + ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n", + __func__, volt, max_uV); + + min_uV = max(volt - 300000, 2700000); + max_uV = min(max_uV + 200000, 3600000); + + /* + * Due to a limitation in the current implementation of + * regulator_set_voltage_triplet() which is taking the lowest + * voltage possible if below the target, search for a suitable + * voltage in two steps and try to stay close to vmmc + * with a 0.3V tolerance at first. + */ + if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc, + min_uV, volt, max_uV)) + return 0; + + return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc, + 2700000, volt, 3600000); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc); + #endif /* CONFIG_REGULATOR */ int mmc_regulator_get_supply(struct mmc_host *mmc) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index cfb3c99..8673ffe 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -411,6 +411,7 @@ int mmc_regulator_get_ocrmask(struct regulator *supply); int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, unsigned short vdd_bit); +int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios); #else static inline int mmc_regulator_get_ocrmask(struct regulator *supply) { @@ -423,6 +424,12 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc, { return 0; } + +static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + return -EINVAL; +} #endif int mmc_regulator_get_supply(struct mmc_host *mmc); -- cgit v0.10.2 From e0848f5d294c703917260a6228cc08b8be46c527 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 12 Oct 2015 14:48:26 +0200 Subject: mmc: dw_mmc: Use mmc_regulator_set_vqmmc in start_signal_voltage_switch We've introduced a new helper in the MMC core: mmc_regulator_set_vqmmc(). Let's use this in dw_mmc. Using this new helper has some advantages: 1. We get the mmc_regulator_set_vqmmc() behavior of trying to match VQMMC and VMMC when the signal voltage is 3.3V. This ensures max compatibility. 2. We get rid of a few more warnings when probing unsupported voltages. 3. We get rid of some non-dw_mmc specific code in dw_mmc. Signed-off-by: Douglas Anderson Signed-off-by: Heiko Stuebner Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index fcbf552..b1b7e7f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1279,7 +1279,6 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) const struct dw_mci_drv_data *drv_data = host->drv_data; u32 uhs; u32 v18 = SDMMC_UHS_18V << slot->id; - int min_uv, max_uv; int ret; if (drv_data && drv_data->switch_voltage) @@ -1291,22 +1290,18 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) * does no harm but you need to set the regulator directly. Try both. */ uhs = mci_readl(host, UHS_REG); - if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { - min_uv = 2700000; - max_uv = 3600000; + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) uhs &= ~v18; - } else { - min_uv = 1700000; - max_uv = 1950000; + else uhs |= v18; - } + if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); + ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { dev_dbg(&mmc->class_dev, - "Regulator set error %d: %d - %d\n", - ret, min_uv, max_uv); + "Regulator set error %d - %s V\n", + ret, uhs & v18 ? "1.8" : "3.3"); return ret; } } -- cgit v0.10.2 From f8085bbada3b124b5b39b61bb1a7a0a78c24604b Mon Sep 17 00:00:00 2001 From: Alexandru M Stan Date: Mon, 12 Oct 2015 14:48:27 +0200 Subject: mmc: dw_mmc-rockchip: dt-binding: Add tuning related things Add ciu_drive, ciu_sample clocks and default-sample-phase. This will later be used by tuning code. We do not touch ciu_drive (and by extension define default-drive-phase). Drive phase is mostly used to define minimum hold times, while one could write some code to determine what phase meets the minimum hold time (ex 10 degrees) this will not work with the current clock phase framework (which floors angles, so we'll get 0 deg, and there's no way to know what resolution the floors happen at). We assume that the default drive angles set by the hardware are good enough. Signed-off-by: Alexandru M Stan Signed-off-by: Heiko Stuebner Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt index c327c2d..3dc13b6 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt @@ -14,6 +14,19 @@ Required Properties: before RK3288 - "rockchip,rk3288-dw-mshc": for Rockchip RK3288 +Optional Properties: +* clocks: from common clock binding: if ciu_drive and ciu_sample are + specified in clock-names, should contain handles to these clocks. + +* clock-names: Apart from the clock-names described in synopsys-dw-mshc.txt + two more clocks "ciu-drive" and "ciu-sample" are supported. They are used + to control the clock phases, "ciu-sample" is required for tuning high- + speed modes. + +* rockchip,default-sample-phase: The default phase to set ciu_sample at + probing, low speeds or in case where all phases work at tuning time. + If not specified 0 deg will be used. + Example: rkdwmmc0@12200000 { -- cgit v0.10.2 From cbb79e43c82635840cdcbf71b1d1c374e2c3a025 Mon Sep 17 00:00:00 2001 From: Alexandru M Stan Date: Mon, 12 Oct 2015 14:48:28 +0200 Subject: mmc: dw_mmc-rockchip: MMC tuning with the clock phase framework This algorithm will try 1 degree increments, since there's no way to tell what resolution the underlying phase code uses. As an added bonus, doing many tunings yields better results since some tests are run more than once (ex: if the underlying driver uses 45 degree increments, the tuning code will try the same angle more than once). It will then construct a list of good phase ranges (even ranges that cross 360/0), will pick the biggest range then it will set the sample_clk to the middle of that range. We do not touch ciu_drive (and by extension define default-drive-phase). Drive phase is mostly used to define minimum hold times, while one could write some code to determine what phase meets the minimum hold time (ex 10 degrees) this will not work with the current clock phase framework (which floors angles, so we'll get 0 deg, and there's no way to know what resolution the floors happen at). We assume that the default drive angles set by the hardware are good enough. If a device has device specific code (like exynos) then that will still take precedence, otherwise this new code will execute. If the device wants to tune, but has no sample_clk defined we'll return EIO with an error message. Signed-off-by: Alexandru M Stan Signed-off-by: Heiko Stuebner Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index bc76aa2..4b3650f 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -13,12 +13,19 @@ #include #include #include +#include #include "dw_mmc.h" #include "dw_mmc-pltfm.h" #define RK3288_CLKGEN_DIV 2 +struct dw_mci_rockchip_priv_data { + struct clk *drv_clk; + struct clk *sample_clk; + int default_sample_phase; +}; + static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; @@ -33,6 +40,7 @@ static int dw_mci_rk3288_setup_clock(struct dw_mci *host) static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) { + struct dw_mci_rockchip_priv_data *priv = host->priv; int ret; unsigned int cclkin; u32 bus_hz; @@ -66,6 +74,158 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) /* force dw_mci_setup_bus() */ host->current_speed = 0; } + + /* Make sure we use phases which we can enumerate with */ + if (!IS_ERR(priv->sample_clk)) + clk_set_phase(priv->sample_clk, priv->default_sample_phase); +} + +#define NUM_PHASES 360 +#define TUNING_ITERATION_TO_PHASE(i) (DIV_ROUND_UP((i) * 360, NUM_PHASES)) + +static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot->host; + struct dw_mci_rockchip_priv_data *priv = host->priv; + struct mmc_host *mmc = slot->mmc; + int ret = 0; + int i; + bool v, prev_v = 0, first_v; + struct range_t { + int start; + int end; /* inclusive */ + }; + struct range_t *ranges; + unsigned int range_count = 0; + int longest_range_len = -1; + int longest_range = -1; + int middle_phase; + + if (IS_ERR(priv->sample_clk)) { + dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n"); + return -EIO; + } + + ranges = kmalloc_array(NUM_PHASES / 2 + 1, sizeof(*ranges), GFP_KERNEL); + if (!ranges) + return -ENOMEM; + + /* Try each phase and extract good ranges */ + for (i = 0; i < NUM_PHASES; ) { + clk_set_phase(priv->sample_clk, TUNING_ITERATION_TO_PHASE(i)); + + v = !mmc_send_tuning(mmc); + + if (i == 0) + first_v = v; + + if ((!prev_v) && v) { + range_count++; + ranges[range_count-1].start = i; + } + if (v) { + ranges[range_count-1].end = i; + i++; + } else if (i == NUM_PHASES - 1) { + /* No extra skipping rules if we're at the end */ + i++; + } else { + /* + * No need to check too close to an invalid + * one since testing bad phases is slow. Skip + * 20 degrees. + */ + i += DIV_ROUND_UP(20 * NUM_PHASES, 360); + + /* Always test the last one */ + if (i >= NUM_PHASES) + i = NUM_PHASES - 1; + } + + prev_v = v; + } + + if (range_count == 0) { + dev_warn(host->dev, "All phases bad!"); + ret = -EIO; + goto free; + } + + /* wrap around case, merge the end points */ + if ((range_count > 1) && first_v && v) { + ranges[0].start = ranges[range_count-1].start; + range_count--; + } + + if (ranges[0].start == 0 && ranges[0].end == NUM_PHASES - 1) { + clk_set_phase(priv->sample_clk, priv->default_sample_phase); + dev_info(host->dev, "All phases work, using default phase %d.", + priv->default_sample_phase); + goto free; + } + + /* Find the longest range */ + for (i = 0; i < range_count; i++) { + int len = (ranges[i].end - ranges[i].start + 1); + + if (len < 0) + len += NUM_PHASES; + + if (longest_range_len < len) { + longest_range_len = len; + longest_range = i; + } + + dev_dbg(host->dev, "Good phase range %d-%d (%d len)\n", + TUNING_ITERATION_TO_PHASE(ranges[i].start), + TUNING_ITERATION_TO_PHASE(ranges[i].end), + len + ); + } + + dev_dbg(host->dev, "Best phase range %d-%d (%d len)\n", + TUNING_ITERATION_TO_PHASE(ranges[longest_range].start), + TUNING_ITERATION_TO_PHASE(ranges[longest_range].end), + longest_range_len + ); + + middle_phase = ranges[longest_range].start + longest_range_len / 2; + middle_phase %= NUM_PHASES; + dev_info(host->dev, "Successfully tuned phase to %d\n", + TUNING_ITERATION_TO_PHASE(middle_phase)); + + clk_set_phase(priv->sample_clk, + TUNING_ITERATION_TO_PHASE(middle_phase)); + +free: + kfree(ranges); + return ret; +} + +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ + struct device_node *np = host->dev->of_node; + struct dw_mci_rockchip_priv_data *priv; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (of_property_read_u32(np, "rockchip,default-sample-phase", + &priv->default_sample_phase)) + priv->default_sample_phase = 0; + + priv->drv_clk = devm_clk_get(host->dev, "ciu-drive"); + if (IS_ERR(priv->drv_clk)) + dev_dbg(host->dev, "ciu_drv not available\n"); + + priv->sample_clk = devm_clk_get(host->dev, "ciu-sample"); + if (IS_ERR(priv->sample_clk)) + dev_dbg(host->dev, "ciu_sample not available\n"); + + host->priv = priv; + + return 0; } static int dw_mci_rockchip_init(struct dw_mci *host) @@ -95,6 +255,8 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .caps = dw_mci_rk3288_dwmmc_caps, .prepare_command = dw_mci_rockchip_prepare_command, .set_ios = dw_mci_rk3288_set_ios, + .execute_tuning = dw_mci_rk3288_execute_tuning, + .parse_dt = dw_mci_rk3288_parse_dt, .setup_clock = dw_mci_rk3288_setup_clock, .init = dw_mci_rockchip_init, }; -- cgit v0.10.2 From f71ddc5873cb90ffb217de50aed8bdd2fb00fc9f Mon Sep 17 00:00:00 2001 From: Alexandru M Stan Date: Mon, 12 Oct 2015 14:48:29 +0200 Subject: ARM: dts: rockchip: Add drive/sample clocks for rk3288 dw_mmc devices The drive/sample clocks can be phase shifted. The drive clock could be used in a future patch to adjust hold times. The sample clock is used for tuning. Signed-off-by: Alexandru M Stan Signed-off-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 906e938..4e7c6b7 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -222,8 +222,9 @@ sdmmc: dwmmc@ff0c0000 { compatible = "rockchip,rk3288-dw-mshc"; clock-freq-min-max = <400000 150000000>; - clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>; - clock-names = "biu", "ciu"; + clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, + <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0xff0c0000 0x4000>; @@ -233,8 +234,9 @@ sdio0: dwmmc@ff0d0000 { compatible = "rockchip,rk3288-dw-mshc"; clock-freq-min-max = <400000 150000000>; - clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>; - clock-names = "biu", "ciu"; + clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>, + <&cru SCLK_SDIO0_DRV>, <&cru SCLK_SDIO0_SAMPLE>; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0xff0d0000 0x4000>; @@ -244,8 +246,9 @@ sdio1: dwmmc@ff0e0000 { compatible = "rockchip,rk3288-dw-mshc"; clock-freq-min-max = <400000 150000000>; - clocks = <&cru HCLK_SDIO1>, <&cru SCLK_SDIO1>; - clock-names = "biu", "ciu"; + clocks = <&cru HCLK_SDIO1>, <&cru SCLK_SDIO1>, + <&cru SCLK_SDIO1_DRV>, <&cru SCLK_SDIO1_SAMPLE>; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0xff0e0000 0x4000>; @@ -255,8 +258,9 @@ emmc: dwmmc@ff0f0000 { compatible = "rockchip,rk3288-dw-mshc"; clock-freq-min-max = <400000 150000000>; - clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>; - clock-names = "biu", "ciu"; + clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>, + <&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0xff0f0000 0x4000>; -- cgit v0.10.2 From c41d31f71d8889919d2f3becbccd6d2d7a3bd243 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 12 Oct 2015 14:48:30 +0200 Subject: ARM: dts: rockchip: add tuning related settings to veyron devices This allows the tuning code to run and use higher speeds on capable cards. Signed-off-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/arch/arm/boot/dts/rk3288-veyron-sdmmc.dtsi b/arch/arm/boot/dts/rk3288-veyron-sdmmc.dtsi index b5334ec..fec076e 100644 --- a/arch/arm/boot/dts/rk3288-veyron-sdmmc.dtsi +++ b/arch/arm/boot/dts/rk3288-veyron-sdmmc.dtsi @@ -90,7 +90,7 @@ regulators { vccio_sd: LDO_REG4 { regulator-name = "vccio_sd"; - regulator-min-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; regulator-state-mem { regulator-off-in-suspend; @@ -116,7 +116,12 @@ cap-sd-highspeed; card-detect-delay = <200>; cd-gpios = <&gpio7 5 GPIO_ACTIVE_LOW>; + rockchip,default-sample-phase = <90>; num-slots = <1>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; vmmc-supply = <&vcc33_sd>; vqmmc-supply = <&vccio_sd>; }; diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi index 275c78c..860cea0 100644 --- a/arch/arm/boot/dts/rk3288-veyron.dtsi +++ b/arch/arm/boot/dts/rk3288-veyron.dtsi @@ -149,7 +149,9 @@ broken-cd; bus-width = <8>; cap-mmc-highspeed; + rockchip,default-sample-phase = <158>; disable-wp; + mmc-hs200-1_8v; mmc-pwrseq = <&emmc_pwrseq>; non-removable; num-slots = <1>; @@ -355,6 +357,10 @@ num-slots = <1>; pinctrl-names = "default"; pinctrl-0 = <&sdio0_clk &sdio0_cmd &sdio0_bus4>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; vmmc-supply = <&vcc33_sys>; vqmmc-supply = <&vcc18_wl>; }; -- cgit v0.10.2 From 7f8a9a7e98f99e9cd874eaf6ca2f00438c2a7c38 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Sat, 10 Oct 2015 17:08:32 +0100 Subject: mmc: android-goldfish: Allow compiling the driver with COMPILE_TEST The driver depends on GOLDFISH but there isn't a build dependency so it's a good idea to allow the driver to always be built when the COMPILE_TEST option is enabled. That way, the driver can be built with a config generated by make allyesconfig and check if a patch would break the build. Signed-off-by: Luis de Bethencourt Reviewed-by: Jean Delvare Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 12b274a..56f5b14 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -473,7 +473,7 @@ config MMC_DAVINCI config MMC_GOLDFISH tristate "goldfish qemu Multimedia Card Interface support" - depends on GOLDFISH + depends on GOLDFISH || COMPILE_TEST help This selects the Goldfish Multimedia card Interface emulation found on the Goldfish Android virtual device emulation. -- cgit v0.10.2 From 2860d060ac902df0164cc9580f843b101e27a2c6 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 14 Oct 2015 23:53:03 +0300 Subject: mmc: skip reclaiming host on mmc_add_card() error There's little sense in releasing the host on mmc_add_card() error immediately after reclaiming it, so reclaim the host only in case of success. Signed-off-by: Sergei Shtylyov Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 479b84a..c793fda 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2004,14 +2004,13 @@ int mmc_attach_mmc(struct mmc_host *host) mmc_release_host(host); err = mmc_add_card(host->card); - mmc_claim_host(host); if (err) goto remove_card; + mmc_claim_host(host); return 0; remove_card: - mmc_release_host(host); mmc_remove_card(host->card); mmc_claim_host(host); host->card = NULL; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b1b9200..141eaa9 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1243,14 +1243,13 @@ int mmc_attach_sd(struct mmc_host *host) mmc_release_host(host); err = mmc_add_card(host->card); - mmc_claim_host(host); if (err) goto remove_card; + mmc_claim_host(host); return 0; remove_card: - mmc_release_host(host); mmc_remove_card(host->card); host->card = NULL; mmc_claim_host(host); -- cgit v0.10.2 From 76d63c2b59d4a1481bedc65a3ef25e9d1354dca3 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 16 Oct 2015 01:39:39 +0300 Subject: DT: mmc: sh_mmcif: fix "compatible" property text The "compatible" property text contradicts even the example given in the MMCIF binding document itself; moreover, the Renesas MMCIF driver only matches on the generic "compatible" string and doesn't look for the SoC specific strings at all. Thus describe "renesas,sh-mmcif" as a fallback value. Fixes: b4c27763d749 ("mmc: sh_mmcif: Document DT bindings") Signed-off-by: Sergei Shtylyov Acked-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt index d38942f..b907f6d 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt +++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt @@ -6,11 +6,11 @@ and the properties used by the MMCIF device. Required properties: -- compatible: must contain one of the following +- compatible: should be "renesas,mmcif-", "renesas,sh-mmcif" as a + fallback. Examples with are: - "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs - "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs - "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs - - "renesas,sh-mmcif" for the generic MMCIF - clocks: reference to the functional clock -- cgit v0.10.2 From a599579ee8e32daebdfa4d97b24389c680ef5789 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 16 Oct 2015 01:40:16 +0300 Subject: DT: mmc: sh_mmcif: document R8A7794 support Renesas R8A7794 SoC also has the MMCIF controller. Signed-off-by: Sergei Shtylyov Acked-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt index b907f6d..cae29eb 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt +++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt @@ -11,6 +11,7 @@ Required properties: - "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs - "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs - "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs + - "renesas,mmcif-r8a7794" for the MMCIF found in r8a7794 SoCs - clocks: reference to the functional clock -- cgit v0.10.2 From 9e4703df3aa9829a84d6ccf19c6062ba19a8de71 Mon Sep 17 00:00:00 2001 From: yangbo lu Date: Fri, 16 Oct 2015 15:44:03 +0800 Subject: mmc: sdhci-of-esdhc: avoid writing power control register The eSDHC doesn't have a standard power control register, so when writing this register in stack we should do nothing to avoid incorrect operation. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 7b9282b..90e94a0 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -183,6 +183,12 @@ static u32 esdhc_writeb_fixup(struct sdhci_host *host, int shift = (spec_reg & 0x3) * 8; /* + * eSDHC doesn't have a standard power control register, so we do + * nothing here to avoid incorrect operation. + */ + if (spec_reg == SDHCI_POWER_CONTROL) + return old_value; + /* * "DMA select" location is offset 0x28 in SD specification, but on * P5020 or P3041, it's located at 0x29. */ -- cgit v0.10.2 From 3fc7eaef44dbcbcd602b6bcd0ac6efba7a30b108 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:41:23 +0800 Subject: mmc: dw_mmc: Add external dma interface support DesignWare MMC Controller can supports two types of DMA mode: external dma and internal dma. We get a RK312x platform integrated dw_mmc and ARM pl330 dma controller. This patch add edmac ops to support these platforms. I've tested it on RK31xx platform with edmac mode and RK3288 platform with idmac mode. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 56f5b14..ef54084 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -615,15 +615,7 @@ config MMC_DW help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both - PIO and external DMA modes. - -config MMC_DW_IDMAC - bool "Internal DMAC interface" - depends on MMC_DW - help - This selects support for the internal DMAC block within the Synopsys - Designware Mobile Storage IP block. This disables the external DMA - interface. + PIO, internal DMA mode and external DMA mode. config MMC_DW_PLTFM tristate "Synopsys Designware MCI Support as platform device" @@ -652,7 +644,6 @@ config MMC_DW_K3 tristate "K3 specific extensions for Synopsys DW Memory Card Interface" depends on MMC_DW select MMC_DW_PLTFM - select MMC_DW_IDMAC help This selects support for Hisilicon K3 SoC specific extensions to the Synopsys DesignWare Memory Card Interface driver. Select this option diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index ec6dbcd..7e1d13b 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -59,6 +59,8 @@ int dw_mci_pltfm_register(struct platform_device *pdev, host->pdata = pdev->dev.platform_data; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* Get registers' physical base address */ + host->phy_regs = (void *)(regs->start); host->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(host->regs)) return PTR_ERR(host->regs); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b1b7e7f..7fe0315 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -56,7 +56,6 @@ #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ #define DW_MCI_FREQ_MIN 400000 /* unit: HZ */ -#ifdef CONFIG_MMC_DW_IDMAC #define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ @@ -102,7 +101,6 @@ struct idmac_desc { /* Each descriptor can transfer up to 4KB of data in chained mode */ #define DW_MCI_DESC_DATA_LENGTH 0x1000 -#endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); @@ -407,7 +405,6 @@ static int dw_mci_get_dma_dir(struct mmc_data *data) return DMA_FROM_DEVICE; } -#ifdef CONFIG_MMC_DW_IDMAC static void dw_mci_dma_cleanup(struct dw_mci *host) { struct mmc_data *data = host->data; @@ -445,12 +442,21 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host) mci_writel(host, BMOD, temp); } -static void dw_mci_idmac_complete_dma(struct dw_mci *host) +static void dw_mci_dmac_complete_dma(void *arg) { + struct dw_mci *host = arg; struct mmc_data *data = host->data; dev_vdbg(host->dev, "DMA complete\n"); + if ((host->use_dma == TRANS_MODE_EDMAC) && + data && (data->flags & MMC_DATA_READ)) + /* Invalidate cache after read */ + dma_sync_sg_for_cpu(mmc_dev(host->cur_slot->mmc), + data->sg, + data->sg_len, + DMA_FROM_DEVICE); + host->dma_ops->cleanup(host); /* @@ -564,7 +570,7 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, wmb(); /* drain writebuffer */ } -static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) +static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) { u32 temp; @@ -589,6 +595,8 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) /* Start it running */ mci_writel(host, PLDMND, 1); + + return 0; } static int dw_mci_idmac_init(struct dw_mci *host) @@ -669,10 +677,112 @@ static const struct dw_mci_dma_ops dw_mci_idmac_ops = { .init = dw_mci_idmac_init, .start = dw_mci_idmac_start_dma, .stop = dw_mci_idmac_stop_dma, - .complete = dw_mci_idmac_complete_dma, + .complete = dw_mci_dmac_complete_dma, + .cleanup = dw_mci_dma_cleanup, +}; + +static void dw_mci_edmac_stop_dma(struct dw_mci *host) +{ + dmaengine_terminate_all(host->dms->ch); +} + +static int dw_mci_edmac_start_dma(struct dw_mci *host, + unsigned int sg_len) +{ + struct dma_slave_config cfg; + struct dma_async_tx_descriptor *desc = NULL; + struct scatterlist *sgl = host->data->sg; + const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; + u32 sg_elems = host->data->sg_len; + u32 fifoth_val; + u32 fifo_offset = host->fifo_reg - host->regs; + int ret = 0; + + /* Set external dma config: burst size, burst width */ + cfg.dst_addr = (dma_addr_t)(host->phy_regs + fifo_offset); + cfg.src_addr = cfg.dst_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + /* Match burst msize with external dma config */ + fifoth_val = mci_readl(host, FIFOTH); + cfg.dst_maxburst = mszs[(fifoth_val >> 28) & 0x7]; + cfg.src_maxburst = cfg.dst_maxburst; + + if (host->data->flags & MMC_DATA_WRITE) + cfg.direction = DMA_MEM_TO_DEV; + else + cfg.direction = DMA_DEV_TO_MEM; + + ret = dmaengine_slave_config(host->dms->ch, &cfg); + if (ret) { + dev_err(host->dev, "Failed to config edmac.\n"); + return -EBUSY; + } + + desc = dmaengine_prep_slave_sg(host->dms->ch, sgl, + sg_len, cfg.direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(host->dev, "Can't prepare slave sg.\n"); + return -EBUSY; + } + + /* Set dw_mci_dmac_complete_dma as callback */ + desc->callback = dw_mci_dmac_complete_dma; + desc->callback_param = (void *)host; + dmaengine_submit(desc); + + /* Flush cache before write */ + if (host->data->flags & MMC_DATA_WRITE) + dma_sync_sg_for_device(mmc_dev(host->cur_slot->mmc), sgl, + sg_elems, DMA_TO_DEVICE); + + dma_async_issue_pending(host->dms->ch); + + return 0; +} + +static int dw_mci_edmac_init(struct dw_mci *host) +{ + /* Request external dma channel */ + host->dms = kzalloc(sizeof(struct dw_mci_dma_slave), GFP_KERNEL); + if (!host->dms) + return -ENOMEM; + + host->dms->ch = dma_request_slave_channel(host->dev, "rx-tx"); + if (!host->dms->ch) { + dev_err(host->dev, + "Failed to get external DMA channel %d\n", + host->dms->ch->chan_id); + kfree(host->dms); + host->dms = NULL; + return -ENXIO; + } + + return 0; +} + +static void dw_mci_edmac_exit(struct dw_mci *host) +{ + if (host->dms) { + if (host->dms->ch) { + dma_release_channel(host->dms->ch); + host->dms->ch = NULL; + } + kfree(host->dms); + host->dms = NULL; + } +} + +static const struct dw_mci_dma_ops dw_mci_edmac_ops = { + .init = dw_mci_edmac_init, + .exit = dw_mci_edmac_exit, + .start = dw_mci_edmac_start_dma, + .stop = dw_mci_edmac_stop_dma, + .complete = dw_mci_dmac_complete_dma, .cleanup = dw_mci_dma_cleanup, }; -#endif /* CONFIG_MMC_DW_IDMAC */ static int dw_mci_pre_dma_transfer(struct dw_mci *host, struct mmc_data *data, @@ -752,7 +862,6 @@ static void dw_mci_post_req(struct mmc_host *mmc, static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) { -#ifdef CONFIG_MMC_DW_IDMAC unsigned int blksz = data->blksz; const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; u32 fifo_width = 1 << host->data_shift; @@ -760,6 +869,10 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; int idx = ARRAY_SIZE(mszs) - 1; + /* pio should ship this scenario */ + if (!host->use_dma) + return; + tx_wmark = (host->fifo_depth) / 2; tx_wmark_invers = host->fifo_depth - tx_wmark; @@ -788,7 +901,6 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) done: fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark); mci_writel(host, FIFOTH, fifoth_val); -#endif } static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) @@ -850,10 +962,12 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) host->using_dma = 1; - dev_vdbg(host->dev, - "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", - (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, - sg_len); + if (host->use_dma == TRANS_MODE_IDMAC) + dev_vdbg(host->dev, + "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", + (unsigned long)host->sg_cpu, + (unsigned long)host->sg_dma, + sg_len); /* * Decide the MSIZE and RX/TX Watermark. @@ -875,7 +989,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) mci_writel(host, INTMASK, temp); spin_unlock_irqrestore(&host->irq_lock, irqflags); - host->dma_ops->start(host, sg_len); + if (host->dma_ops->start(host, sg_len)) { + /* We can't do DMA */ + dev_err(host->dev, "%s: failed to start DMA.\n", __func__); + return -ENODEV; + } return 0; } @@ -2338,15 +2456,17 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } -#ifdef CONFIG_MMC_DW_IDMAC - /* Handle DMA interrupts */ + if (host->use_dma != TRANS_MODE_IDMAC) + return IRQ_HANDLED; + + /* Handle IDMA interrupts */ if (host->dma_64bit_address == 1) { pending = mci_readl(host, IDSTS64); if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete(host); + host->dma_ops->complete((void *)host); } } else { pending = mci_readl(host, IDSTS); @@ -2354,10 +2474,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete(host); + host->dma_ops->complete((void *)host); } } -#endif return IRQ_HANDLED; } @@ -2466,13 +2585,21 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) goto err_host_allocated; /* Useful defaults if platform data is unset. */ - if (host->use_dma) { + if (host->use_dma == TRANS_MODE_IDMAC) { mmc->max_segs = host->ring_size; mmc->max_blk_size = 65536; mmc->max_seg_size = 0x1000; mmc->max_req_size = mmc->max_seg_size * host->ring_size; mmc->max_blk_count = mmc->max_req_size / 512; + } else if (host->use_dma == TRANS_MODE_EDMAC) { + mmc->max_segs = 64; + mmc->max_blk_size = 65536; + mmc->max_blk_count = 65535; + mmc->max_req_size = + mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; } else { + /* TRANS_MODE_PIO */ mmc->max_segs = 64; mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ mmc->max_blk_count = 512; @@ -2512,38 +2639,74 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { int addr_config; - /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */ - addr_config = (mci_readl(host, HCON) >> 27) & 0x01; - - if (addr_config == 1) { - /* host supports IDMAC in 64-bit address mode */ - host->dma_64bit_address = 1; - dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); - if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) - dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64)); - } else { - /* host supports IDMAC in 32-bit address mode */ - host->dma_64bit_address = 0; - dev_info(host->dev, "IDMAC supports 32-bit address mode.\n"); - } + struct device *dev = host->dev; + struct device_node *np = dev->of_node; - /* Alloc memory for sg translation */ - host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, - &host->sg_dma, GFP_KERNEL); - if (!host->sg_cpu) { - dev_err(host->dev, "%s: could not alloc DMA memory\n", - __func__); + /* + * Check tansfer mode from HCON[17:16] + * Clear the ambiguous description of dw_mmc databook: + * 2b'00: No DMA Interface -> Actually means using Internal DMA block + * 2b'01: DesignWare DMA Interface -> Synopsys DW-DMA block + * 2b'10: Generic DMA Interface -> non-Synopsys generic DMA block + * 2b'11: Non DW DMA Interface -> pio only + * Compared to DesignWare DMA Interface, Generic DMA Interface has a + * simpler request/acknowledge handshake mechanism and both of them + * are regarded as external dma master for dw_mmc. + */ + host->use_dma = SDMMC_GET_TRANS_MODE(mci_readl(host, HCON)); + if (host->use_dma == DMA_INTERFACE_IDMA) { + host->use_dma = TRANS_MODE_IDMAC; + } else if (host->use_dma == DMA_INTERFACE_DWDMA || + host->use_dma == DMA_INTERFACE_GDMA) { + host->use_dma = TRANS_MODE_EDMAC; + } else { goto no_dma; } /* Determine which DMA interface to use */ -#ifdef CONFIG_MMC_DW_IDMAC - host->dma_ops = &dw_mci_idmac_ops; - dev_info(host->dev, "Using internal DMA controller.\n"); -#endif + if (host->use_dma == TRANS_MODE_IDMAC) { + /* + * Check ADDR_CONFIG bit in HCON to find + * IDMAC address bus width + */ + addr_config = (mci_readl(host, HCON) >> 27) & 0x01; + + if (addr_config == 1) { + /* host supports IDMAC in 64-bit address mode */ + host->dma_64bit_address = 1; + dev_info(host->dev, + "IDMAC supports 64-bit address mode.\n"); + if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) + dma_set_coherent_mask(host->dev, + DMA_BIT_MASK(64)); + } else { + /* host supports IDMAC in 32-bit address mode */ + host->dma_64bit_address = 0; + dev_info(host->dev, + "IDMAC supports 32-bit address mode.\n"); + } - if (!host->dma_ops) - goto no_dma; + /* Alloc memory for sg translation */ + host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, + &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) { + dev_err(host->dev, + "%s: could not alloc DMA memory\n", + __func__); + goto no_dma; + } + + host->dma_ops = &dw_mci_idmac_ops; + dev_info(host->dev, "Using internal DMA controller.\n"); + } else { + /* TRANS_MODE_EDMAC: check dma bindings again */ + if ((of_property_count_strings(np, "dma-names") < 0) || + (!of_find_property(np, "dmas", NULL))) { + goto no_dma; + } + host->dma_ops = &dw_mci_edmac_ops; + dev_info(host->dev, "Using external DMA controller.\n"); + } if (host->dma_ops->init && host->dma_ops->start && host->dma_ops->stop && host->dma_ops->cleanup) { @@ -2557,12 +2720,11 @@ static void dw_mci_init_dma(struct dw_mci *host) goto no_dma; } - host->use_dma = 1; return; no_dma: dev_info(host->dev, "Using PIO mode.\n"); - host->use_dma = 0; + host->use_dma = TRANS_MODE_PIO; } static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) @@ -2645,10 +2807,9 @@ static bool dw_mci_reset(struct dw_mci *host) } } -#if IS_ENABLED(CONFIG_MMC_DW_IDMAC) - /* It is also recommended that we reset and reprogram idmac */ - dw_mci_idmac_reset(host); -#endif + if (host->use_dma == TRANS_MODE_IDMAC) + /* It is also recommended that we reset and reprogram idmac */ + dw_mci_idmac_reset(host); ret = true; @@ -3062,6 +3223,9 @@ EXPORT_SYMBOL(dw_mci_remove); */ int dw_mci_suspend(struct dw_mci *host) { + if (host->use_dma && host->dma_ops->exit) + host->dma_ops->exit(host); + return 0; } EXPORT_SYMBOL(dw_mci_suspend); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 8ce4674..811d467 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -148,6 +148,12 @@ #define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ ((r) & 0xFFF) << 16 | \ ((t) & 0xFFF)) +/* HCON register defines */ +#define DMA_INTERFACE_IDMA (0x0) +#define DMA_INTERFACE_DWDMA (0x1) +#define DMA_INTERFACE_GDMA (0x2) +#define DMA_INTERFACE_NODMA (0x3) +#define SDMMC_GET_TRANS_MODE(x) (((x)>>16) & 0x3) /* Internal DMAC interrupt defines */ #define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_NI BIT(8) diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 134c574..f67b2ec 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -16,6 +16,7 @@ #include #include +#include #define MAX_MCI_SLOTS 2 @@ -40,6 +41,17 @@ enum { struct mmc_data; +enum { + TRANS_MODE_PIO = 0, + TRANS_MODE_IDMAC, + TRANS_MODE_EDMAC +}; + +struct dw_mci_dma_slave { + struct dma_chan *ch; + enum dma_transfer_direction direction; +}; + /** * struct dw_mci - MMC controller state shared between all slots * @lock: Spinlock protecting the queue and associated data. @@ -154,7 +166,14 @@ struct dw_mci { dma_addr_t sg_dma; void *sg_cpu; const struct dw_mci_dma_ops *dma_ops; + /* For idmac */ unsigned int ring_size; + + /* For edmac */ + struct dw_mci_dma_slave *dms; + /* Registers's physical base address */ + void *phy_regs; + u32 cmd_status; u32 data_status; u32 stop_cmdr; @@ -208,8 +227,8 @@ struct dw_mci { struct dw_mci_dma_ops { /* DMA Ops */ int (*init)(struct dw_mci *host); - void (*start)(struct dw_mci *host, unsigned int sg_len); - void (*complete)(struct dw_mci *host); + int (*start)(struct dw_mci *host, unsigned int sg_len); + void (*complete)(void *host); void (*stop)(struct dw_mci *host); void (*cleanup)(struct dw_mci *host); void (*exit)(struct dw_mci *host); -- cgit v0.10.2 From 70692752af12e2dbae48685b1d07fe7bdb2fab31 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:41:37 +0800 Subject: mmc: dw_mmc: use macro for HCON register operations This patch add some macros for HCON register operations to make code more readable. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7fe0315..6e600e8 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2669,7 +2669,7 @@ static void dw_mci_init_dma(struct dw_mci *host) * Check ADDR_CONFIG bit in HCON to find * IDMAC address bus width */ - addr_config = (mci_readl(host, HCON) >> 27) & 0x01; + addr_config = SDMMC_GET_ADDR_CONFIG(mci_readl(host, HCON)); if (addr_config == 1) { /* host supports IDMAC in 64-bit address mode */ @@ -3046,7 +3046,7 @@ int dw_mci_probe(struct dw_mci *host) * Get the host data width - this assumes that HCON has been set with * the correct values. */ - i = (mci_readl(host, HCON) >> 7) & 0x7; + i = SDMMC_GET_HDATA_WIDTH(mci_readl(host, HCON)); if (!i) { host->push_data = dw_mci_push_data16; host->pull_data = dw_mci_pull_data16; @@ -3128,7 +3128,7 @@ int dw_mci_probe(struct dw_mci *host) if (host->pdata->num_slots) host->num_slots = host->pdata->num_slots; else - host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; + host->num_slots = SDMMC_GET_SLOT_NUM(mci_readl(host, HCON)); /* * Enable interrupts for command done, data over, data empty, diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 811d467..f2a88d4 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -154,6 +154,9 @@ #define DMA_INTERFACE_GDMA (0x2) #define DMA_INTERFACE_NODMA (0x3) #define SDMMC_GET_TRANS_MODE(x) (((x)>>16) & 0x3) +#define SDMMC_GET_SLOT_NUM(x) ((((x)>>1) & 0x1F) + 1) +#define SDMMC_GET_HDATA_WIDTH(x) (((x)>>7) & 0x7) +#define SDMMC_GET_ADDR_CONFIG(x) (((x)>>27) & 0x1) /* Internal DMAC interrupt defines */ #define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_NI BIT(8) -- cgit v0.10.2 From 87ffa7d30d0be24f4bcf08005f145903c5ef55b7 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:41:50 +0800 Subject: Documentation: synopsys-dw-mshc: add bindings for idmac and edmac synopsys-dw-mshc supports three types of transfer mode. We add bindings and description for how to use them at runtime. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt index 346c609..8636f5a 100644 --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt @@ -75,6 +75,12 @@ Optional properties: * vmmc-supply: The phandle to the regulator to use for vmmc. If this is specified we'll defer probe until we can find this regulator. +* dmas: List of DMA specifiers with the controller specific format as described + in the generic DMA client binding. Refer to dma.txt for details. + +* dma-names: request names for generic DMA client binding. Must be "rx-tx". + Refer to dma.txt for details. + Aliases: - All the MSHC controller nodes should be represented in the aliases node using @@ -95,6 +101,23 @@ board specific portions as listed below. #size-cells = <0>; }; +[board specific internal DMA resources] + + dwmmc0@12200000 { + clock-frequency = <400000000>; + clock-freq-min-max = <400000 200000000>; + num-slots = <1>; + broken-cd; + fifo-depth = <0x80>; + card-detect-delay = <200>; + vmmc-supply = <&buck8>; + bus-width = <8>; + cap-mmc-highspeed; + cap-sd-highspeed; + }; + +[board specific generic DMA request binding] + dwmmc0@12200000 { clock-frequency = <400000000>; clock-freq-min-max = <400000 200000000>; @@ -106,4 +129,6 @@ board specific portions as listed below. bus-width = <8>; cap-mmc-highspeed; cap-sd-highspeed; + dmas = <&pdma 12>; + dma-names = "rx-tx"; }; -- cgit v0.10.2 From b13af8f9685e85d0a0939aae8c5fce53f8e90186 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:42:05 +0800 Subject: mips: pistachio_defconfig: remove CONFIG_MMC_DW_IDMAC DesignWare MMC Controller's transfer mode should be decided at runtime instead of compile-time. So we remove this config option and read dw_mmc's register to select DMA master. Signed-off-by: Shawn Lin Acked-by: Govindraj Raja Acked-by: Ralf Baechle Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/arch/mips/configs/pistachio_defconfig b/arch/mips/configs/pistachio_defconfig index 642b509..8b74291 100644 --- a/arch/mips/configs/pistachio_defconfig +++ b/arch/mips/configs/pistachio_defconfig @@ -257,7 +257,6 @@ CONFIG_MMC=y CONFIG_MMC_BLOCK_MINORS=16 CONFIG_MMC_TEST=m CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_RTC_CLASS=y -- cgit v0.10.2 From 005a5243aab6a2d45910233593bf9cba4f207bf2 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:42:20 +0800 Subject: arc: axs10x_defconfig: remove CONFIG_MMC_DW_IDMAC DesignWare MMC Controller's transfer mode should be decided at runtime instead of compile-time. So we remove this config option and read dw_mmc's register to select DMA master. Signed-off-by: Shawn Lin Acked-by: Vineet Gupta Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/arch/arc/configs/axs101_defconfig b/arch/arc/configs/axs101_defconfig index 562dac6..c92c0ef 100644 --- a/arch/arc/configs/axs101_defconfig +++ b/arch/arc/configs/axs101_defconfig @@ -89,7 +89,6 @@ CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_EXT3_FS=y CONFIG_EXT4_FS=y diff --git a/arch/arc/configs/axs103_defconfig b/arch/arc/configs/axs103_defconfig index 83a6d8d..cfac24e 100644 --- a/arch/arc/configs/axs103_defconfig +++ b/arch/arc/configs/axs103_defconfig @@ -95,7 +95,6 @@ CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_EXT3_FS=y CONFIG_EXT4_FS=y diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig index f1e1c84..9922a11 100644 --- a/arch/arc/configs/axs103_smp_defconfig +++ b/arch/arc/configs/axs103_smp_defconfig @@ -96,7 +96,6 @@ CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_EXT3_FS=y CONFIG_EXT4_FS=y -- cgit v0.10.2 From 391ce1e0eb0af2cb5d85273a142061fb77ecf135 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:42:37 +0800 Subject: arm: exynos_defconfig: remove CONFIG_MMC_DW_IDMAC DesignWare MMC Controller's transfer mode should be decided at runtime instead of compile-time. So we remove this config option and read dw_mmc's register to select DMA master. Signed-off-by: Shawn Lin Acked-by: Krzysztof Kozlowski Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index 1ff2bfa..13ba48c 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -166,7 +166,6 @@ CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_S3C=y CONFIG_MMC_SDHCI_S3C_DMA=y CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y CONFIG_MMC_DW_EXYNOS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_MAX77686=y -- cgit v0.10.2 From fdd36e530e0f29b86aaa2013e0d9dee0964ecf6f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:42:49 +0800 Subject: arm: hisi_defconfig: remove CONFIG_MMC_DW_IDMAC DesignWare MMC Controller's transfer mode should be decided at runtime instead of compile-time. So we remove this config option and read dw_mmc's register to select DMA master. Signed-off-by: Shawn Lin Acked-by: Wei Xu Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/arch/arm/configs/hisi_defconfig b/arch/arm/configs/hisi_defconfig index 5997dbc..b2e340b 100644 --- a/arch/arm/configs/hisi_defconfig +++ b/arch/arm/configs/hisi_defconfig @@ -69,7 +69,6 @@ CONFIG_NOP_USB_XCEIV=y CONFIG_MMC=y CONFIG_RTC_CLASS=y CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y CONFIG_MMC_DW_PLTFM=y CONFIG_RTC_DRV_PL031=y CONFIG_DMADEVICES=y -- cgit v0.10.2 From b3b78f2cb2eeca47f3b81dd795fd341a337363c4 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 16 Sep 2015 14:43:01 +0800 Subject: arm: lpc18xx_defconfig: remove CONFIG_MMC_DW_IDMAC DesignWare MMC Controller's transfer mode should be decided at runtime instead of compile-time. So we remove this config option and read dw_mmc's register to select DMA master. Signed-off-by: Shawn Lin Acked-by: Joachim Eastwood Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/arch/arm/configs/lpc18xx_defconfig b/arch/arm/configs/lpc18xx_defconfig index 1c47f86..b7e8cda 100644 --- a/arch/arm/configs/lpc18xx_defconfig +++ b/arch/arm/configs/lpc18xx_defconfig @@ -119,7 +119,6 @@ CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_ROOT_HUB_TT=y CONFIG_MMC=y CONFIG_MMC_DW=y -CONFIG_MMC_DW_IDMAC=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_PCA9532=y -- cgit v0.10.2 From 4fd4c0655b82b0f81841b915ef1e70e2141d4a38 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 21 Oct 2015 11:15:45 +0300 Subject: mmc: sdhci-pci: Add more PCI IDs for Intel controllers Add PCI IDs for Intel host controllers Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index b6f6117..cf7ad45 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1117,6 +1117,54 @@ static const struct pci_device_id pci_ids[] = { }, { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BXT_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BXT_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BXT_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_APL_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_APL_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_APL_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + + { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, .subvendor = PCI_ANY_ID, diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index cd4f4d7..d1a0b4d 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -25,6 +25,12 @@ #define PCI_DEVICE_ID_INTEL_SPT_SDIO 0x9d2c #define PCI_DEVICE_ID_INTEL_SPT_SD 0x9d2d #define PCI_DEVICE_ID_INTEL_DNV_EMMC 0x19db +#define PCI_DEVICE_ID_INTEL_BXT_SD 0x0aca +#define PCI_DEVICE_ID_INTEL_BXT_EMMC 0x0acc +#define PCI_DEVICE_ID_INTEL_BXT_SDIO 0x0ad0 +#define PCI_DEVICE_ID_INTEL_APL_SD 0x5aca +#define PCI_DEVICE_ID_INTEL_APL_EMMC 0x5acc +#define PCI_DEVICE_ID_INTEL_APL_SDIO 0x5ad0 /* * PCI registers -- cgit v0.10.2 From e839b134761444e5988d5eee1ef54a0ff2327344 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 21 Oct 2015 11:15:46 +0300 Subject: mmc: sdhci-acpi: Add more ACPI HIDs for Intel controllers Add ACPI HIDs for Intel host controllers including one supporting HS400. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 78aa16a..f6047fc 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -207,7 +207,9 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC | + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, .probe_slot = sdhci_acpi_emmc_probe_slot, }; @@ -239,6 +241,9 @@ struct sdhci_acpi_uid_slot { }; static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = { + { "80865ACA", NULL, &sdhci_acpi_slot_int_sd }, + { "80865ACC", NULL, &sdhci_acpi_slot_int_emmc }, + { "80865AD0", NULL, &sdhci_acpi_slot_int_sdio }, { "80860F14" , "1" , &sdhci_acpi_slot_int_emmc }, { "80860F14" , "3" , &sdhci_acpi_slot_int_sd }, { "80860F16" , NULL, &sdhci_acpi_slot_int_sd }, @@ -253,6 +258,9 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = { }; static const struct acpi_device_id sdhci_acpi_ids[] = { + { "80865ACA" }, + { "80865ACC" }, + { "80865AD0" }, { "80860F14" }, { "80860F16" }, { "INT33BB" }, -- cgit v0.10.2 From a7cedab6ccd7f02c86476ae52760a5d0482806fa Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 20 Oct 2015 16:50:06 -0500 Subject: mmc: omap_hsmmc: Enable omap_hsmmc for Keystone 2 Enable omap_hsmmc for Keystone 2 architecture which reuses the HSMMC IP found on OMAP platforms. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ef54084..af71de5 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -366,7 +366,7 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" depends on HAS_DMA - depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST help This selects the TI OMAP High Speed Multimedia card Interface. If you have an omap2plus board with a Multimedia Card slot, -- cgit v0.10.2 From 794f15782d9f12aa5a43f796680a5fc4f2c424e3 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:21 +0800 Subject: mmc: core: Add DT bindings for eMMC hardware reset support Sometime only need set MMC_CAP_HW_RESET for one of MMC hosts, So set it in device tree is better. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 0384fc3..f693baf 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -37,6 +37,7 @@ Optional properties: - sd-uhs-sdr104: SD UHS SDR104 speed is supported - sd-uhs-ddr50: SD UHS DDR50 speed is supported - cap-power-off-card: powering off the card is safe +- cap-mmc-hw-reset: eMMC hardware reset is supported - cap-sdio-irq: enable SDIO IRQ signalling on this interface - full-pwr-cycle: full power cycle of the card is supported - mmc-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 970e690..da950c4 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -267,6 +267,8 @@ int mmc_of_parse(struct mmc_host *host) host->caps |= MMC_CAP_UHS_DDR50; if (of_property_read_bool(np, "cap-power-off-card")) host->caps |= MMC_CAP_POWER_OFF_CARD; + if (of_property_read_bool(np, "cap-mmc-hw-reset")) + host->caps |= MMC_CAP_HW_RESET; if (of_property_read_bool(np, "cap-sdio-irq")) host->caps |= MMC_CAP_SDIO_IRQ; if (of_property_read_bool(np, "full-pwr-cycle")) -- cgit v0.10.2 From 2c21d26825d5c1fda1729541d5fad56ee7ef7fa2 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:22 +0800 Subject: mmc: dt-bindings: update Mediatek MMC bindings Add 400Mhz clock source for HS400 mode Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.txt b/Documentation/devicetree/bindings/mmc/mtk-sd.txt index a1adfa4..0120c7f 100644 --- a/Documentation/devicetree/bindings/mmc/mtk-sd.txt +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.txt @@ -17,6 +17,11 @@ Required properties: - vmmc-supply: power to the Core - vqmmc-supply: power to the IO +Optional properties: +- assigned-clocks: PLL of the source clock +- assigned-clock-parents: parent of source clock, used for HS400 mode to get 400Mhz source clock +- hs400-ds-delay: HS400 DS delay setting + Examples: mmc0: mmc@11230000 { compatible = "mediatek,mt8173-mmc", "mediatek,mt8135-mmc"; @@ -24,9 +29,13 @@ mmc0: mmc@11230000 { interrupts = ; vmmc-supply = <&mt6397_vemc_3v3_reg>; vqmmc-supply = <&mt6397_vio18_reg>; - clocks = <&pericfg CLK_PERI_MSDC30_0>, <&topckgen CLK_TOP_MSDC50_0_H_SEL>; + clocks = <&pericfg CLK_PERI_MSDC30_0>, + <&topckgen CLK_TOP_MSDC50_0_H_SEL>; clock-names = "source", "hclk"; pinctrl-names = "default", "state_uhs"; pinctrl-0 = <&mmc0_pins_default>; pinctrl-1 = <&mmc0_pins_uhs>; + assigned-clocks = <&topckgen CLK_TOP_MSDC50_0_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_MSDCPLL_D2>; + hs400-ds-delay = <0x14015>; }; -- cgit v0.10.2 From 726a9aac31a89d5a02614ca8f2b3f644df621c27 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:23 +0800 Subject: mmc: mediatek: make cmd_ints_mask to const cmd_ints_mask and data_ints_mask are constant value, so make it to const Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index b2e89d3..8b3e15d 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -353,7 +353,10 @@ static void msdc_reset_hw(struct msdc_host *host) static void msdc_cmd_next(struct msdc_host *host, struct mmc_request *mrq, struct mmc_command *cmd); -static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | +static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR | + MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY | + MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO; +static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR | MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT; @@ -725,10 +728,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, if (done) return true; - sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY | - MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | - MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR | - MSDC_INTEN_ACMDTMO); + sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask); if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { @@ -818,10 +818,7 @@ static void msdc_start_command(struct msdc_host *host, rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd); mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); - sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY | - MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | - MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR | - MSDC_INTEN_ACMDTMO); + sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask); writel(cmd->arg, host->base + SDC_ARG); writel(rawcmd, host->base + SDC_CMD); } -- cgit v0.10.2 From 6e622947e149fa9fd62e3609aab010935c8e10bd Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:24 +0800 Subject: mmc: mediatek: change the argument "ddr" to "timing" use the ios->timing directly is better It can reflect current timing and do settings by timing Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 8b3e15d..c877ded 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -297,7 +297,7 @@ struct msdc_host { u32 mclk; /* mmc subsystem clock frequency */ u32 src_clk_freq; /* source clock frequency */ u32 sclk; /* SD/MS bus clock frequency */ - bool ddr; + unsigned char timing; bool vqmmc_enabled; struct msdc_save_para save_para; /* used when gate HCLK */ }; @@ -488,7 +488,7 @@ static void msdc_ungate_clock(struct msdc_host *host) cpu_relax(); } -static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz) +static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) { u32 mode; u32 flags; @@ -504,7 +504,8 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz) flags = readl(host->base + MSDC_INTEN); sdr_clr_bits(host->base + MSDC_INTEN, flags); - if (ddr) { /* may need to modify later */ + if (timing == MMC_TIMING_UHS_DDR50 || + timing == MMC_TIMING_MMC_DDR52) { mode = 0x2; /* ddr mode and use divisor */ if (hz >= (host->src_clk_freq >> 2)) { div = 0; /* mean div = 1/4 */ @@ -535,12 +536,12 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz) cpu_relax(); host->sclk = sclk; host->mclk = hz; - host->ddr = ddr; + host->timing = timing; /* need because clk changed. */ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); sdr_set_bits(host->base + MSDC_INTEN, flags); - dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr); + dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing); } static inline u32 msdc_cmd_find_resp(struct msdc_host *host, @@ -1158,14 +1159,9 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct msdc_host *host = mmc_priv(mmc); int ret; - u32 ddr = 0; pm_runtime_get_sync(host->dev); - if (ios->timing == MMC_TIMING_UHS_DDR50 || - ios->timing == MMC_TIMING_MMC_DDR52) - ddr = 1; - msdc_set_buswidth(host, ios->bus_width); /* Suspend/Resume will do power off/on */ @@ -1202,8 +1198,8 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; } - if (host->mclk != ios->clock || host->ddr != ddr) - msdc_set_mclk(host, ddr, ios->clock); + if (host->mclk != ios->clock || host->timing != ios->timing) + msdc_set_mclk(host, ios->timing, ios->clock); end: pm_runtime_mark_last_busy(host->dev); -- cgit v0.10.2 From 62b0d27af85ef1b1729c2ed675c12020e1fbf10f Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:25 +0800 Subject: mmc: mediatek: fix got GPD checksum error interrupt when data transfer Even if we only use one gpd, we need alloc 2 gpd and make the gpd->next pointer to the second gpd, or may get gpd checksum error, this was checked by hardware Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index c877ded..3858163 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -1145,11 +1145,14 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma) struct mt_bdma_desc *bd = dma->bd; int i; - memset(gpd, 0, sizeof(struct mt_gpdma_desc)); + memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2); gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */ gpd->ptr = (u32)dma->bd_addr; /* physical address */ - + /* gpd->next is must set for desc DMA + * That's why must alloc 2 gpd structure. + */ + gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc); memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM); for (i = 0; i < (MAX_BD_NUM - 1); i++) bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1); @@ -1306,7 +1309,7 @@ static int msdc_drv_probe(struct platform_device *pdev) host->timeout_clks = 3 * 1048576; host->dma.gpd = dma_alloc_coherent(&pdev->dev, - sizeof(struct mt_gpdma_desc), + 2 * sizeof(struct mt_gpdma_desc), &host->dma.gpd_addr, GFP_KERNEL); host->dma.bd = dma_alloc_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc), @@ -1347,7 +1350,7 @@ release: release_mem: if (host->dma.gpd) dma_free_coherent(&pdev->dev, - sizeof(struct mt_gpdma_desc), + 2 * sizeof(struct mt_gpdma_desc), host->dma.gpd, host->dma.gpd_addr); if (host->dma.bd) dma_free_coherent(&pdev->dev, -- cgit v0.10.2 From c9b5061e7728da1f7192ecaf3ffab4a7300920dc Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:26 +0800 Subject: mmc: mediatek: add implement of ops->hw_reset() add implement of ops->hw_reset() for eMMC Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 3858163..5627644 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -64,6 +64,7 @@ #define SDC_RESP2 0x48 #define SDC_RESP3 0x4c #define SDC_BLK_NUM 0x50 +#define EMMC_IOCON 0x7c #define SDC_ACMD_RESP 0x80 #define MSDC_DMA_SA 0x90 #define MSDC_DMA_CTRL 0x98 @@ -1209,6 +1210,15 @@ end: pm_runtime_put_autosuspend(host->dev); } +static void msdc_hw_reset(struct mmc_host *mmc) +{ + struct msdc_host *host = mmc_priv(mmc); + + sdr_set_bits(host->base + EMMC_IOCON, 1); + udelay(10); /* 10us is enough */ + sdr_clr_bits(host->base + EMMC_IOCON, 1); +} + static struct mmc_host_ops mt_msdc_ops = { .post_req = msdc_post_req, .pre_req = msdc_pre_req, @@ -1216,6 +1226,7 @@ static struct mmc_host_ops mt_msdc_ops = { .set_ios = msdc_ops_set_ios, .start_signal_voltage_switch = msdc_ops_switch_volt, .card_busy = msdc_card_busy, + .hw_reset = msdc_hw_reset, }; static int msdc_drv_probe(struct platform_device *pdev) -- cgit v0.10.2 From 9979dbe5158899b556eb772b7335e29417ac0ddd Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:28 +0800 Subject: mmc: mmc: extend the mmc_send_tuning() The mmc_execute_tuning() has already prepared the opcode, there is no need to prepare it again at mmc_send_tuning(), and, there is a BUG of mmc_send_tuning() to determine the opcode by bus width, assume eMMC was running at HS200, 4bit mode, then the mmc_send_tuning() will overwrite the opcode from CMD21 to CMD19, then got error. in addition, extend an argument of "cmd_error" to allow getting if there was cmd error when tune response. Signed-off-by: Chaotian Jing [Ulf: Rebased patch] Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 57f3a21..1f44426 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -588,7 +588,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, } EXPORT_SYMBOL_GPL(mmc_switch); -int mmc_send_tuning(struct mmc_host *host) +int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error) { struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; @@ -598,16 +598,13 @@ int mmc_send_tuning(struct mmc_host *host) const u8 *tuning_block_pattern; int size, err = 0; u8 *data_buf; - u32 opcode; if (ios->bus_width == MMC_BUS_WIDTH_8) { tuning_block_pattern = tuning_blk_pattern_8bit; size = sizeof(tuning_blk_pattern_8bit); - opcode = MMC_SEND_TUNING_BLOCK_HS200; } else if (ios->bus_width == MMC_BUS_WIDTH_4) { tuning_block_pattern = tuning_blk_pattern_4bit; size = sizeof(tuning_blk_pattern_4bit); - opcode = MMC_SEND_TUNING_BLOCK; } else return -EINVAL; @@ -638,6 +635,9 @@ int mmc_send_tuning(struct mmc_host *host) mmc_wait_for_req(host, &mrq); + if (cmd_error) + *cmd_error = cmd.error; + if (cmd.error) { err = cmd.error; goto out; diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 1e75309..3a7e835 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -446,7 +446,7 @@ out: return loc; } -static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) +static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { struct dw_mci *host = slot->host; struct dw_mci_exynos_priv_data *priv = host->priv; @@ -461,7 +461,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) mci_writel(host, TMOUT, ~0); smpl = dw_mci_exynos_move_next_clksmpl(host); - if (!mmc_send_tuning(mmc)) + if (!mmc_send_tuning(mmc, opcode, NULL)) candiates |= (1 << smpl); } while (start_smpl != smpl); diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 4b3650f..9becebe 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -83,7 +83,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) #define NUM_PHASES 360 #define TUNING_ITERATION_TO_PHASE(i) (DIV_ROUND_UP((i) * 360, NUM_PHASES)) -static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot) +static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { struct dw_mci *host = slot->host; struct dw_mci_rockchip_priv_data *priv = host->priv; @@ -114,7 +114,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot) for (i = 0; i < NUM_PHASES; ) { clk_set_phase(priv->sample_clk, TUNING_ITERATION_TO_PHASE(i)); - v = !mmc_send_tuning(mmc); + v = !mmc_send_tuning(mmc, opcode, NULL); if (i == 0) first_v = v; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6e600e8..63eefea 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1540,7 +1540,7 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) int err = -EINVAL; if (drv_data && drv_data->execute_tuning) - err = drv_data->execute_tuning(slot); + err = drv_data->execute_tuning(slot, opcode); return err; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index f2a88d4..11cc848 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -290,7 +290,7 @@ struct dw_mci_drv_data { void (*prepare_command)(struct dw_mci *host, u32 *cmdr); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); - int (*execute_tuning)(struct dw_mci_slot *slot); + int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode); int (*prepare_hs400_tuning)(struct dw_mci *host, struct mmc_ios *ios); int (*switch_voltage)(struct mmc_host *mmc, diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 886d230..1f1582f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -759,7 +759,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) min = ESDHC_TUNE_CTRL_MIN; while (min < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, min); - if (!mmc_send_tuning(host->mmc)) + if (!mmc_send_tuning(host->mmc, opcode, NULL)) break; min += ESDHC_TUNE_CTRL_STEP; } @@ -768,7 +768,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) max = min + ESDHC_TUNE_CTRL_STEP; while (max < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, max); - if (mmc_send_tuning(host->mmc)) { + if (mmc_send_tuning(host->mmc, opcode, NULL)) { max -= ESDHC_TUNE_CTRL_STEP; break; } @@ -778,7 +778,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) /* use average delay to get the best timing */ avg = (min + max) / 2; esdhc_prepare_tuning(host, avg); - ret = mmc_send_tuning(host->mmc); + ret = mmc_send_tuning(host->mmc, opcode, NULL); esdhc_post_tuning(host); dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4bcee03..4695bee 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -373,7 +373,7 @@ retry: if (rc) return rc; - rc = mmc_send_tuning(mmc); + rc = mmc_send_tuning(mmc, opcode, NULL); if (!rc) { /* Tuning is successful at this tuning point */ tuned_phases[tuned_phase_cnt++] = phase; diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index f5488c4..34866f6 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -98,7 +98,7 @@ retry: clock_setting | phase, SDHCI_CLK_DELAY_SETTING); - if (!mmc_send_tuning(mmc)) { + if (!mmc_send_tuning(mmc, opcode, NULL)) { /* Tuning is successful at this tuning point */ tuned_phase_cnt++; dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 79a31d3..37967b6 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -153,7 +153,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); -extern int mmc_send_tuning(struct mmc_host *host); +extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); #define MMC_ERASE_ARG 0x00000000 -- cgit v0.10.2 From 6397b7f5f405f30f03f67ef829306c5af6d3369b Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Tue, 27 Oct 2015 14:24:29 +0800 Subject: mmc: mediatek: add HS400 support add HS400 mode and tune support Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 5627644..39568cc 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,8 @@ #define MSDC_PATCH_BIT 0xb0 #define MSDC_PATCH_BIT1 0xb4 #define MSDC_PAD_TUNE 0xec +#define PAD_DS_TUNE 0x188 +#define EMMC50_CFG0 0x208 /*--------------------------------------------------------------------------*/ /* Register Mask */ @@ -88,6 +91,7 @@ #define MSDC_CFG_CKSTB (0x1 << 7) /* R */ #define MSDC_CFG_CKDIV (0xff << 8) /* RW */ #define MSDC_CFG_CKMOD (0x3 << 16) /* RW */ +#define MSDC_CFG_HS400_CK_MODE (0x1 << 18) /* RW */ /* MSDC_IOCON mask */ #define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */ @@ -205,6 +209,17 @@ #define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */ #define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */ +#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */ +#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */ + +#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */ +#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */ +#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */ + +#define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0) /* RW */ +#define EMMC50_CFG_CRCSTS_EDGE (0x1 << 3) /* RW */ +#define EMMC50_CFG_CFCSTS_SEL (0x1 << 4) /* RW */ + #define REQ_CMD_EIO (0x1 << 0) #define REQ_CMD_TMO (0x1 << 1) #define REQ_DAT_ERR (0x1 << 2) @@ -220,6 +235,7 @@ #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */ #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */ +#define PAD_DELAY_MAX 32 /* PAD delay cells */ /*--------------------------------------------------------------------------*/ /* Descriptor Structure */ /*--------------------------------------------------------------------------*/ @@ -266,6 +282,14 @@ struct msdc_save_para { u32 pad_tune; u32 patch_bit0; u32 patch_bit1; + u32 pad_ds_tune; + u32 emmc50_cfg0; +}; + +struct msdc_delay_phase { + u8 maxlen; + u8 start; + u8 final_phase; }; struct msdc_host { @@ -300,6 +324,7 @@ struct msdc_host { u32 sclk; /* SD/MS bus clock frequency */ unsigned char timing; bool vqmmc_enabled; + u32 hs400_ds_delay; struct msdc_save_para save_para; /* used when gate HCLK */ }; @@ -505,9 +530,15 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) flags = readl(host->base + MSDC_INTEN); sdr_clr_bits(host->base + MSDC_INTEN, flags); + sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE); if (timing == MMC_TIMING_UHS_DDR50 || - timing == MMC_TIMING_MMC_DDR52) { - mode = 0x2; /* ddr mode and use divisor */ + timing == MMC_TIMING_MMC_DDR52 || + timing == MMC_TIMING_MMC_HS400) { + if (timing == MMC_TIMING_MMC_HS400) + mode = 0x3; + else + mode = 0x2; /* ddr mode and use divisor */ + if (hz >= (host->src_clk_freq >> 2)) { div = 0; /* mean div = 1/4 */ sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */ @@ -516,6 +547,14 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) sclk = (host->src_clk_freq >> 2) / div; div = (div >> 1); } + + if (timing == MMC_TIMING_MMC_HS400 && + hz >= (host->src_clk_freq >> 1)) { + sdr_set_bits(host->base + MSDC_CFG, + MSDC_CFG_HS400_CK_MODE); + sclk = host->src_clk_freq >> 1; + div = 0; /* div is ignore when bit18 is set */ + } } else if (hz >= host->src_clk_freq) { mode = 0x1; /* no divisor */ div = 0; @@ -894,7 +933,7 @@ static void msdc_data_xfer_next(struct msdc_host *host, struct mmc_request *mrq, struct mmc_data *data) { if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && !mrq->stop->error && - (!data->bytes_xfered || !mrq->sbc)) + !mrq->sbc) msdc_start_command(host, mrq, mrq->stop); else msdc_request_done(host, mrq); @@ -940,6 +979,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, if (events & MSDC_INT_DATTMO) data->error = -ETIMEDOUT; + else if (events & MSDC_INT_DATCRCERR) + data->error = -EILSEQ; dev_err(host->dev, "%s: cmd=%d; blocks=%d", __func__, mrq->cmd->opcode, data->blocks); @@ -1111,10 +1152,12 @@ static void msdc_init_hw(struct msdc_host *host) writel(0, host->base + MSDC_PAD_TUNE); writel(0, host->base + MSDC_IOCON); - sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1); - writel(0x403c004f, host->base + MSDC_PATCH_BIT); + sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0); + writel(0x403c0046, host->base + MSDC_PATCH_BIT); sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1); writel(0xffff0089, host->base + MSDC_PATCH_BIT1); + sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL); + /* Configure to enable SDIO mode. * it's must otherwise sdio cmd5 failed */ @@ -1172,6 +1215,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: if (!IS_ERR(mmc->supply.vmmc)) { + msdc_init_hw(host); ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); if (ret) { @@ -1210,6 +1254,190 @@ end: pm_runtime_put_autosuspend(host->dev); } +static u32 test_delay_bit(u32 delay, u32 bit) +{ + bit %= PAD_DELAY_MAX; + return delay & (1 << bit); +} + +static int get_delay_len(u32 delay, u32 start_bit) +{ + int i; + + for (i = 0; i < (PAD_DELAY_MAX - start_bit); i++) { + if (test_delay_bit(delay, start_bit + i) == 0) + return i; + } + return PAD_DELAY_MAX - start_bit; +} + +static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay) +{ + int start = 0, len = 0; + int start_final = 0, len_final = 0; + u8 final_phase = 0xff; + struct msdc_delay_phase delay_phase; + + if (delay == 0) { + dev_err(host->dev, "phase error: [map:%x]\n", delay); + delay_phase.final_phase = final_phase; + return delay_phase; + } + + while (start < PAD_DELAY_MAX) { + len = get_delay_len(delay, start); + if (len_final < len) { + start_final = start; + len_final = len; + } + start += len ? len : 1; + if (len >= 8 && start_final < 4) + break; + } + + /* The rule is that to find the smallest delay cell */ + if (start_final == 0) + final_phase = (start_final + len_final / 3) % PAD_DELAY_MAX; + else + final_phase = (start_final + len_final / 2) % PAD_DELAY_MAX; + dev_info(host->dev, "phase: [map:%x] [maxlen:%d] [final:%d]\n", + delay, len_final, final_phase); + + delay_phase.maxlen = len_final; + delay_phase.start = start_final; + delay_phase.final_phase = final_phase; + return delay_phase; +} + +static int msdc_tune_response(struct mmc_host *mmc, u32 opcode) +{ + struct msdc_host *host = mmc_priv(mmc); + u32 rise_delay = 0, fall_delay = 0; + struct msdc_delay_phase final_rise_delay, final_fall_delay; + u8 final_delay, final_maxlen; + int cmd_err; + int i; + + sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL); + for (i = 0 ; i < PAD_DELAY_MAX; i++) { + sdr_set_field(host->base + MSDC_PAD_TUNE, + MSDC_PAD_TUNE_CMDRDLY, i); + mmc_send_tuning(mmc, opcode, &cmd_err); + if (!cmd_err) + rise_delay |= (1 << i); + } + + sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL); + for (i = 0; i < PAD_DELAY_MAX; i++) { + sdr_set_field(host->base + MSDC_PAD_TUNE, + MSDC_PAD_TUNE_CMDRDLY, i); + mmc_send_tuning(mmc, opcode, &cmd_err); + if (!cmd_err) + fall_delay |= (1 << i); + } + + final_rise_delay = get_best_delay(host, rise_delay); + final_fall_delay = get_best_delay(host, fall_delay); + + final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen); + if (final_maxlen == final_rise_delay.maxlen) { + sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL); + sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY, + final_rise_delay.final_phase); + final_delay = final_rise_delay.final_phase; + } else { + sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL); + sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY, + final_fall_delay.final_phase); + final_delay = final_fall_delay.final_phase; + } + + return final_delay == 0xff ? -EIO : 0; +} + +static int msdc_tune_data(struct mmc_host *mmc, u32 opcode) +{ + struct msdc_host *host = mmc_priv(mmc); + u32 rise_delay = 0, fall_delay = 0; + struct msdc_delay_phase final_rise_delay, final_fall_delay; + u8 final_delay, final_maxlen; + int i, ret; + + sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL); + sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL); + for (i = 0 ; i < PAD_DELAY_MAX; i++) { + sdr_set_field(host->base + MSDC_PAD_TUNE, + MSDC_PAD_TUNE_DATRRDLY, i); + ret = mmc_send_tuning(mmc, opcode, NULL); + if (!ret) + rise_delay |= (1 << i); + } + + sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL); + sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL); + for (i = 0; i < PAD_DELAY_MAX; i++) { + sdr_set_field(host->base + MSDC_PAD_TUNE, + MSDC_PAD_TUNE_DATRRDLY, i); + ret = mmc_send_tuning(mmc, opcode, NULL); + if (!ret) + fall_delay |= (1 << i); + } + + final_rise_delay = get_best_delay(host, rise_delay); + final_fall_delay = get_best_delay(host, fall_delay); + + final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen); + /* Rising edge is more stable, prefer to use it */ + if (final_rise_delay.maxlen >= 10) + final_maxlen = final_rise_delay.maxlen; + if (final_maxlen == final_rise_delay.maxlen) { + sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL); + sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL); + sdr_set_field(host->base + MSDC_PAD_TUNE, + MSDC_PAD_TUNE_DATRRDLY, + final_rise_delay.final_phase); + final_delay = final_rise_delay.final_phase; + } else { + sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL); + sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL); + sdr_set_field(host->base + MSDC_PAD_TUNE, + MSDC_PAD_TUNE_DATRRDLY, + final_fall_delay.final_phase); + final_delay = final_fall_delay.final_phase; + } + + return final_delay == 0xff ? -EIO : 0; +} + +static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct msdc_host *host = mmc_priv(mmc); + int ret; + + pm_runtime_get_sync(host->dev); + ret = msdc_tune_response(mmc, opcode); + if (ret == -EIO) { + dev_err(host->dev, "Tune response fail!\n"); + goto out; + } + ret = msdc_tune_data(mmc, opcode); + if (ret == -EIO) + dev_err(host->dev, "Tune data fail!\n"); + +out: + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); + return ret; +} + +static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct msdc_host *host = mmc_priv(mmc); + + writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE); + return 0; +} + static void msdc_hw_reset(struct mmc_host *mmc) { struct msdc_host *host = mmc_priv(mmc); @@ -1226,6 +1454,8 @@ static struct mmc_host_ops mt_msdc_ops = { .set_ios = msdc_ops_set_ios, .start_signal_voltage_switch = msdc_ops_switch_volt, .card_busy = msdc_card_busy, + .execute_tuning = msdc_execute_tuning, + .prepare_hs400_tuning = msdc_prepare_hs400_tuning, .hw_reset = msdc_hw_reset, }; @@ -1300,6 +1530,11 @@ static int msdc_drv_probe(struct platform_device *pdev) goto host_free; } + if (!of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay", + &host->hs400_ds_delay)) + dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n", + host->hs400_ds_delay); + host->dev = &pdev->dev; host->mmc = mmc; host->src_clk_freq = clk_get_rate(host->src_clk); @@ -1410,6 +1645,8 @@ static void msdc_save_reg(struct msdc_host *host) host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE); host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT); host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1); + host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE); + host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0); } static void msdc_restore_reg(struct msdc_host *host) @@ -1420,6 +1657,8 @@ static void msdc_restore_reg(struct msdc_host *host) writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE); writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT); writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1); + writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE); + writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0); } static int msdc_runtime_suspend(struct device *dev) -- cgit v0.10.2 From 1c6e58d83615678875bf52747f8841c49291dfcf Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 21 Oct 2015 17:15:40 +0200 Subject: mmc: pwrseq: Use highest priority for eMMC restart handler The pwrseq_emmc driver does a eMMC card reset before a system reboot to allow broken or limited ROM boot-loaders (that don't have an eMMC reset logic) to be able to read the second stage from the eMMC. But this has to be called before a system reboot handler and while most of them use the priority 128, there are other restart handlers (such as the syscon-reboot one) that use a higher priority. So, use the highest priority to make sure that the eMMC hw is reset before a system reboot. Signed-off-by: Javier Martinez Canillas Tested-by: Markus Reichl Tested-by: Anand Moon Reviewed-by: Alim Akhtar Tested-by: Douglas Anderson Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c index 137c97f..ad4f94e 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -84,11 +84,11 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, /* * register reset handler to ensure emmc reset also from - * emergency_reboot(), priority 129 schedules it just before - * system reboot + * emergency_reboot(), priority 255 is the highest priority + * so it will be executed before any system reboot handler. */ pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; - pwrseq->reset_nb.priority = 129; + pwrseq->reset_nb.priority = 255; register_restart_handler(&pwrseq->reset_nb); pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; -- cgit v0.10.2 From 4539d36ef2bbf2ee1519e7cfb51c6fdddda3e4e6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 22 Oct 2015 22:53:46 +0300 Subject: mmc: dw_mmc: NULL dereference in error message The "host->dms->ch" pointer is NULL here so we can't use it to print the error message. Signed-off-by: Dan Carpenter Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 63eefea..57a9577 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -752,9 +752,7 @@ static int dw_mci_edmac_init(struct dw_mci *host) host->dms->ch = dma_request_slave_channel(host->dev, "rx-tx"); if (!host->dms->ch) { - dev_err(host->dev, - "Failed to get external DMA channel %d\n", - host->dms->ch->chan_id); + dev_err(host->dev, "Failed to get external DMA channel.\n"); kfree(host->dms); host->dms = NULL; return -ENXIO; -- cgit v0.10.2 From 98daafd8a09d9a514bc2166709731d659037003b Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 21 Oct 2015 19:49:41 +0900 Subject: mmc: dw_mmc: fix the CardThreshold boundary at CardThrCtl register According to DesignWare DoC file, CardThreshold bit should be bit[27:16]. So it's correct to use (0xFFF << 16), not (0x1FFF << 16). Signed-off-by: Jaehoon Chung Reviewed-by: Alim Akhtar Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 11cc848..f695b58 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -172,7 +172,7 @@ /* Version ID register define */ #define SDMMC_GET_VERID(x) ((x) & 0xFFFF) /* Card read threshold */ -#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) +#define SDMMC_SET_RD_THLD(v, x) (((v) & 0xFFF) << 16 | (x)) #define SDMMC_UHS_18V BIT(0) /* All ctrl reset bits */ #define SDMMC_CTRL_ALL_RESET_FLAGS \ -- cgit v0.10.2 From 7cc8d580228cc712edcf7a1856a3bdb38c164e83 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 21 Oct 2015 19:49:42 +0900 Subject: mmc: dw_mmc: fix the wrong setting for UHS-DDR50 mode When card is running with DDR mode, dwmmc needs to set DDR_REG bit at UHS_REG register. Before this patch, dwmmc controller doesn't consider this. If this patch is not applied, CRC or other error shoulds be occurred. Signed-off-by: Jaehoon Chung Reviewed-by: Alim Akhtar Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 57a9577..7a6cedb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1293,6 +1293,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* DDR mode set */ if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_HS400) regs |= ((0x1 << slot->id) << 16); else -- cgit v0.10.2