diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/debugfs.c | 51 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 7 | ||||
-rw-r--r-- | drivers/mmc/core/queue.c | 6 | ||||
-rw-r--r-- | drivers/mmc/core/slot-gpio.c | 6 | ||||
-rw-r--r-- | drivers/mmc/host/Kconfig | 12 | ||||
-rw-r--r-- | drivers/mmc/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/host/atmel-mci.c | 9 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc-starfive.c | 137 | ||||
-rw-r--r-- | drivers/mmc/host/jz4740_mmc.c | 15 | ||||
-rw-r--r-- | drivers/mmc/host/mmc_hsq.c | 22 | ||||
-rw-r--r-- | drivers/mmc/host/mmc_hsq.h | 11 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 3 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.h | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 52 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-npcm.c | 94 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pci-core.c | 5 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pci-o2micro.c | 30 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.c | 38 | ||||
-rw-r--r-- | drivers/mmc/host/vub300.c | 22 |
19 files changed, 348 insertions, 175 deletions
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 2c97b94aab..1642ea72d2 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -12,9 +12,12 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/fault-inject.h> +#include <linux/time.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> #include "core.h" #include "card.h" @@ -298,6 +301,49 @@ static const struct file_operations mmc_err_stats_fops = { .release = single_release, }; +static int mmc_caps_get(void *data, u64 *val) +{ + *val = *(u32 *)data; + return 0; +} + +static int mmc_caps_set(void *data, u64 val) +{ + u32 *caps = data; + u32 diff = *caps ^ val; + u32 allowed = MMC_CAP_AGGRESSIVE_PM | + MMC_CAP_SD_HIGHSPEED | + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_UHS | + MMC_CAP_DDR; + + if (diff & ~allowed) + return -EINVAL; + + *caps = val; + + return 0; +} + +static int mmc_caps2_set(void *data, u64 val) +{ + u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V; + u32 *caps = data; + u32 diff = *caps ^ val; + + if (diff & ~allowed) + return -EINVAL; + + *caps = val; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(mmc_caps_fops, mmc_caps_get, mmc_caps_set, + "0x%08llx\n"); +DEFINE_DEBUGFS_ATTRIBUTE(mmc_caps2_fops, mmc_caps_get, mmc_caps2_set, + "0x%08llx\n"); + void mmc_add_host_debugfs(struct mmc_host *host) { struct dentry *root; @@ -306,8 +352,9 @@ void mmc_add_host_debugfs(struct mmc_host *host) host->debugfs_root = root; debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops); - debugfs_create_x32("caps", S_IRUSR, root, &host->caps); - debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2); + debugfs_create_file("caps", 0600, root, &host->caps, &mmc_caps_fops); + debugfs_create_file("caps2", 0600, root, &host->caps2, + &mmc_caps2_fops); debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host, &mmc_clock_fops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a46ce0868f..705942edac 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -419,7 +419,6 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT]; card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; - mmc_select_card_type(card); card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT]; card->ext_csd.raw_erase_timeout_mult = @@ -1732,6 +1731,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_set_erase_size(card); } + /* + * Reselect the card type since host caps could have been changed when + * debugging even if the card is not new. + */ + mmc_select_card_type(card); + /* Enable ERASE_GRP_DEF. This bit is lost after a reset or power off. */ if (card->ext_csd.rev >= 3) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index b396e39007..a0a2412f62 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -260,11 +260,7 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx, } break; case MMC_ISSUE_ASYNC: - /* - * For MMC host software queue, we only allow 2 requests in - * flight to avoid a long latency. - */ - if (host->hsq_enabled && mq->in_flight[issue_type] > 2) { + if (host->hsq_enabled && mq->in_flight[issue_type] > host->hsq_depth) { spin_unlock_irq(&mq->lock); return BLK_STS_RESOURCE; } diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 2a2d949a93..39f45c2b6d 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -75,11 +75,15 @@ EXPORT_SYMBOL(mmc_gpio_set_cd_irq); int mmc_gpio_get_ro(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; + int cansleep; if (!ctx || !ctx->ro_gpio) return -ENOSYS; - return gpiod_get_value_cansleep(ctx->ro_gpio); + cansleep = gpiod_cansleep(ctx->ro_gpio); + return cansleep ? + gpiod_get_value_cansleep(ctx->ro_gpio) : + gpiod_get_value(ctx->ro_gpio); } EXPORT_SYMBOL(mmc_gpio_get_ro); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index bc7e2ad370..81f2c4e052 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -429,6 +429,14 @@ config MMC_SDHCI_IPROC If unsure, say N. +config MMC_SDHCI_NPCM + tristate "Secure Digital Host Controller Interface support for NPCM" + depends on ARCH_NPCM || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + help + This provides support for the SD/eMMC controller found in + NPCM BMC family SoCs. + config MMC_MESON_GX tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support" depends on ARCH_MESON|| COMPILE_TEST @@ -677,9 +685,9 @@ config MMC_SDHI_SYS_DMAC config MMC_SDHI_INTERNAL_DMAC tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering" - depends on ARM64 || ARCH_R7S9210 || ARCH_R8A77470 || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST depends on MMC_SDHI - default MMC_SDHI if (ARM64 || ARCH_R7S9210 || ARCH_R8A77470) + default MMC_SDHI if ARCH_RENESAS help This provides DMA support for SDHI SD/SDIO controllers using on-chip bus mastering. This supports the controllers diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index a693fa3d3f..d0be4465f3 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o +obj-$(CONFIG_MMC_SDHCI_NPCM) += sdhci-npcm.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 535783c431..dba826db73 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -227,6 +227,7 @@ struct mci_slot_pdata { /** * struct mci_platform_data - board-specific MMC/SDcard configuration * @dma_slave: DMA slave interface to use in data transfers. + * @dma_filter: Filtering function to filter the DMA channel * @slot: Per-slot configuration data. */ struct mci_platform_data { @@ -674,8 +675,10 @@ atmci_of_init(struct platform_device *pdev) "cd", GPIOD_IN, "cd-gpios"); err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].detect_pin); if (err) { - if (err != -ENOENT) + if (err != -ENOENT) { + of_node_put(cnp); return ERR_PTR(err); + } pdata->slot[slot_id].detect_pin = NULL; } @@ -687,8 +690,10 @@ atmci_of_init(struct platform_device *pdev) "wp", GPIOD_IN, "wp-gpios"); err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].wp_pin); if (err) { - if (err != -ENOENT) + if (err != -ENOENT) { + of_node_put(cnp); return ERR_PTR(err); + } pdata->slot[slot_id].wp_pin = NULL; } } diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c index fd05a648a8..b4d81ef0f3 100644 --- a/drivers/mmc/host/dw_mmc-starfive.c +++ b/drivers/mmc/host/dw_mmc-starfive.c @@ -5,6 +5,7 @@ * Copyright (c) 2022 StarFive Technology Co., Ltd. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/mfd/syscon.h> @@ -20,13 +21,7 @@ #define ALL_INT_CLR 0x1ffff #define MAX_DELAY_CHAIN 32 -struct starfive_priv { - struct device *dev; - struct regmap *reg_syscon; - u32 syscon_offset; - u32 syscon_shift; - u32 syscon_mask; -}; +#define STARFIVE_SMPL_PHASE GENMASK(20, 16) static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios) { @@ -44,117 +39,65 @@ static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } +static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase) +{ + /* change driver phase and sample phase */ + u32 reg_value = mci_readl(host, UHS_REG_EXT); + + /* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */ + reg_value &= ~STARFIVE_SMPL_PHASE; + reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase); + mci_writel(host, UHS_REG_EXT, reg_value); + + /* We should delay 1ms wait for timing setting finished. */ + mdelay(1); +} + static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { static const int grade = MAX_DELAY_CHAIN; struct dw_mci *host = slot->host; - struct starfive_priv *priv = host->priv; - int rise_point = -1, fall_point = -1; - int err, prev_err = 0; - int i; - bool found = 0; - u32 regval; - - /* - * Use grade as the max delay chain, and use the rise_point and - * fall_point to ensure the best sampling point of a data input - * signals. - */ - for (i = 0; i < grade; i++) { - regval = i << priv->syscon_shift; - err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, - priv->syscon_mask, regval); - if (err) - return err; + int smpl_phase, smpl_raise = -1, smpl_fall = -1; + int ret; + + for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) { + dw_mci_starfive_set_sample_phase(host, smpl_phase); mci_writel(host, RINTSTS, ALL_INT_CLR); - err = mmc_send_tuning(slot->mmc, opcode, NULL); - if (!err) - found = 1; + ret = mmc_send_tuning(slot->mmc, opcode, NULL); - if (i > 0) { - if (err && !prev_err) - fall_point = i - 1; - if (!err && prev_err) - rise_point = i; + if (!ret && smpl_raise < 0) { + smpl_raise = smpl_phase; + } else if (ret && smpl_raise >= 0) { + smpl_fall = smpl_phase - 1; + break; } - - if (rise_point != -1 && fall_point != -1) - goto tuning_out; - - prev_err = err; - err = 0; } -tuning_out: - if (found) { - if (rise_point == -1) - rise_point = 0; - if (fall_point == -1) - fall_point = grade - 1; - if (fall_point < rise_point) { - if ((rise_point + fall_point) > - (grade - 1)) - i = fall_point / 2; - else - i = (rise_point + grade - 1) / 2; - } else { - i = (rise_point + fall_point) / 2; - } - - regval = i << priv->syscon_shift; - err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, - priv->syscon_mask, regval); - if (err) - return err; - mci_writel(host, RINTSTS, ALL_INT_CLR); + if (smpl_phase >= grade) + smpl_fall = grade - 1; - dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i); - } else { + if (smpl_raise < 0) { + smpl_phase = 0; dev_err(host->dev, "No valid delay chain! use default\n"); - err = -EINVAL; + ret = -EINVAL; + goto out; } - mci_writel(host, RINTSTS, ALL_INT_CLR); - return err; -} - -static int dw_mci_starfive_parse_dt(struct dw_mci *host) -{ - struct of_phandle_args args; - struct starfive_priv *priv; - int ret; - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + smpl_phase = (smpl_raise + smpl_fall) / 2; + dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase); + ret = 0; - ret = of_parse_phandle_with_fixed_args(host->dev->of_node, - "starfive,sysreg", 3, 0, &args); - if (ret) { - dev_err(host->dev, "Failed to parse starfive,sysreg\n"); - return -EINVAL; - } - - priv->reg_syscon = syscon_node_to_regmap(args.np); - of_node_put(args.np); - if (IS_ERR(priv->reg_syscon)) - return PTR_ERR(priv->reg_syscon); - - priv->syscon_offset = args.args[0]; - priv->syscon_shift = args.args[1]; - priv->syscon_mask = args.args[2]; - - host->priv = priv; - - return 0; +out: + dw_mci_starfive_set_sample_phase(host, smpl_phase); + mci_writel(host, RINTSTS, ALL_INT_CLR); + return ret; } static const struct dw_mci_drv_data starfive_data = { .common_caps = MMC_CAP_CMD23, .set_ios = dw_mci_starfive_set_ios, - .parse_dt = dw_mci_starfive_parse_dt, .execute_tuning = dw_mci_starfive_execute_tuning, }; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index f379ce5b58..6a45991ca0 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -18,9 +18,10 @@ #include <linux/mmc/host.h> #include <linux/mmc/slot-gpio.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/scatterlist.h> @@ -1040,7 +1041,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev) int ret; struct mmc_host *mmc; struct jz4740_mmc_host *host; - const struct of_device_id *match; mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); if (!mmc) { @@ -1050,13 +1050,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); - match = of_match_device(jz4740_mmc_of_match, &pdev->dev); - if (match) { - host->version = (enum jz4740_mmc_version)match->data; - } else { - /* JZ4740 should be the only one using legacy probe */ - host->version = JZ_MMC_JZ4740; - } + /* Default if no match is JZ4740 */ + host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev); ret = mmc_of_parse(mmc); if (ret) { @@ -1200,7 +1195,7 @@ static struct platform_driver jz4740_mmc_driver = { .driver = { .name = "jz4740-mmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = of_match_ptr(jz4740_mmc_of_match), + .of_match_table = jz4740_mmc_of_match, .pm = pm_sleep_ptr(&jz4740_mmc_pm_ops), }, }; diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c index 424dc7b078..79836705c1 100644 --- a/drivers/mmc/host/mmc_hsq.c +++ b/drivers/mmc/host/mmc_hsq.c @@ -21,6 +21,25 @@ static void mmc_hsq_retry_handler(struct work_struct *work) mmc->ops->request(mmc, hsq->mrq); } +static void mmc_hsq_modify_threshold(struct mmc_hsq *hsq) +{ + struct mmc_host *mmc = hsq->mmc; + struct mmc_request *mrq; + unsigned int tag, need_change = 0; + + mmc->hsq_depth = HSQ_NORMAL_DEPTH; + for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) { + mrq = hsq->slot[tag].mrq; + if (mrq && mrq->data && + (mrq->data->blksz * mrq->data->blocks == 4096) && + (mrq->data->flags & MMC_DATA_WRITE) && + (++need_change == 2)) { + mmc->hsq_depth = HSQ_PERFORMANCE_DEPTH; + break; + } + } +} + static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) { struct mmc_host *mmc = hsq->mmc; @@ -42,6 +61,8 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) return; } + mmc_hsq_modify_threshold(hsq); + slot = &hsq->slot[hsq->next_tag]; hsq->mrq = slot->mrq; hsq->qcnt--; @@ -337,6 +358,7 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) hsq->mmc = mmc; hsq->mmc->cqe_private = hsq; mmc->cqe_ops = &mmc_hsq_ops; + mmc->hsq_depth = HSQ_NORMAL_DEPTH; for (i = 0; i < HSQ_NUM_SLOTS; i++) hsq->tag_slot[i] = HSQ_INVALID_TAG; diff --git a/drivers/mmc/host/mmc_hsq.h b/drivers/mmc/host/mmc_hsq.h index 1808024fc6..dd352a6ac3 100644 --- a/drivers/mmc/host/mmc_hsq.h +++ b/drivers/mmc/host/mmc_hsq.h @@ -5,6 +5,17 @@ #define HSQ_NUM_SLOTS 64 #define HSQ_INVALID_TAG HSQ_NUM_SLOTS +/* + * For MMC host software queue, we only allow 2 requests in + * flight to avoid a long latency. + */ +#define HSQ_NORMAL_DEPTH 2 +/* + * For 4k random writes, we allow hsq_depth to increase to 5 + * for better performance. + */ +#define HSQ_PERFORMANCE_DEPTH 5 + struct hsq_slot { struct mmc_request *mrq; }; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index dda756a563..e967cca7a1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -249,6 +249,7 @@ static struct variant_data variant_stm32 = { .f_max = 48000000, .pwrreg_clkgate = true, .pwrreg_nopower = true, + .dma_flow_controller = true, .init = mmci_variant_init, }; @@ -1015,7 +1016,7 @@ static int _mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data, .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, .src_maxburst = variant->fifohalfsize >> 2, /* # of words */ .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */ - .device_fc = false, + .device_fc = variant->dma_flow_controller, }; struct dma_chan *chan; struct dma_device *device; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 253197f132..34d9897c28 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -332,6 +332,7 @@ enum mmci_busy_state { * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register * @dma_lli: true if variant has dma link list feature. * @stm32_idmabsize_mask: stm32 sdmmc idma buffer size. + * @dma_flow_controller: use peripheral as flow controller for DMA. */ struct variant_data { unsigned int clkreg; @@ -378,6 +379,7 @@ struct variant_data { u8 dma_lli:1; u32 stm32_idmabsize_mask; u32 stm32_idmabsize_align; + bool dma_flow_controller; void (*init)(struct mmci_host *host); }; diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 3b8030f355..40a6e2f814 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1154,32 +1154,52 @@ static void esdhc_post_tuning(struct sdhci_host *host) writel(reg, host->ioaddr + ESDHC_MIX_CTRL); } +/* + * find the largest pass window, and use the average delay of this + * largest window to get the best timing. + */ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) { int min, max, avg, ret; + int win_length, target_min, target_max, target_win_length; - /* find the mininum delay first which can pass tuning */ min = ESDHC_TUNE_CTRL_MIN; - while (min < ESDHC_TUNE_CTRL_MAX) { - esdhc_prepare_tuning(host, min); - if (!mmc_send_tuning(host->mmc, opcode, NULL)) - break; - min += ESDHC_TUNE_CTRL_STEP; - } - - /* find the maxinum delay which can not pass tuning */ - max = min + ESDHC_TUNE_CTRL_STEP; + max = ESDHC_TUNE_CTRL_MIN; + target_win_length = 0; while (max < ESDHC_TUNE_CTRL_MAX) { - esdhc_prepare_tuning(host, max); - if (mmc_send_tuning(host->mmc, opcode, NULL)) { - max -= ESDHC_TUNE_CTRL_STEP; - break; + /* find the mininum delay first which can pass tuning */ + while (min < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, min); + if (!mmc_send_tuning(host->mmc, opcode, NULL)) + break; + min += ESDHC_TUNE_CTRL_STEP; } - max += ESDHC_TUNE_CTRL_STEP; + + /* find the maxinum delay which can not pass tuning */ + max = min + ESDHC_TUNE_CTRL_STEP; + while (max < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, max); + if (mmc_send_tuning(host->mmc, opcode, NULL)) { + max -= ESDHC_TUNE_CTRL_STEP; + break; + } + max += ESDHC_TUNE_CTRL_STEP; + } + + win_length = max - min + 1; + /* get the largest pass window */ + if (win_length > target_win_length) { + target_win_length = win_length; + target_min = min; + target_max = max; + } + + /* continue to find the next pass window */ + min = max + ESDHC_TUNE_CTRL_STEP; } /* use average delay to get the best timing */ - avg = (min + max) / 2; + avg = (target_min + target_max) / 2; esdhc_prepare_tuning(host, avg); ret = mmc_send_tuning(host->mmc, opcode, NULL); esdhc_post_tuning(host); diff --git a/drivers/mmc/host/sdhci-npcm.c b/drivers/mmc/host/sdhci-npcm.c new file mode 100644 index 0000000000..5bf9d18f36 --- /dev/null +++ b/drivers/mmc/host/sdhci-npcm.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NPCM SDHC MMC host controller driver. + * + * Copyright (c) 2023 Nuvoton Technology corporation. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> + +#include "sdhci-pltfm.h" + +static const struct sdhci_pltfm_data npcm7xx_sdhci_pdata = { + .quirks = SDHCI_QUIRK_DELAY_AFTER_POWER, + .quirks2 = SDHCI_QUIRK2_STOP_WITH_TC | + SDHCI_QUIRK2_NO_1_8_V, +}; + +static const struct sdhci_pltfm_data npcm8xx_sdhci_pdata = { + .quirks = SDHCI_QUIRK_DELAY_AFTER_POWER, + .quirks2 = SDHCI_QUIRK2_STOP_WITH_TC, +}; + +static int npcm_sdhci_probe(struct platform_device *pdev) +{ + const struct sdhci_pltfm_data *data; + struct sdhci_pltfm_host *pltfm_host; + struct device *dev = &pdev->dev; + struct sdhci_host *host; + u32 caps; + int ret; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + host = sdhci_pltfm_init(pdev, data, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + + pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(pltfm_host->clk)) { + ret = PTR_ERR(pltfm_host->clk); + goto err_sdhci; + } + + caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (caps & SDHCI_CAN_DO_8BIT) + host->mmc->caps |= MMC_CAP_8_BIT_DATA; + + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_sdhci; + + ret = sdhci_add_host(host); + if (ret) + goto err_sdhci; + + return 0; + +err_sdhci: + sdhci_pltfm_free(pdev); + return ret; +} + +static const struct of_device_id npcm_sdhci_of_match[] = { + { .compatible = "nuvoton,npcm750-sdhci", .data = &npcm7xx_sdhci_pdata }, + { .compatible = "nuvoton,npcm845-sdhci", .data = &npcm8xx_sdhci_pdata }, + { } +}; +MODULE_DEVICE_TABLE(of, npcm_sdhci_of_match); + +static struct platform_driver npcm_sdhci_driver = { + .driver = { + .name = "npcm-sdhci", + .of_match_table = npcm_sdhci_of_match, + .pm = &sdhci_pltfm_pmops, + }, + .probe = npcm_sdhci_probe, + .remove_new = sdhci_pltfm_remove, +}; +module_platform_driver(npcm_sdhci_driver); + +MODULE_DESCRIPTION("NPCM Secure Digital Host Controller Interface driver"); +MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 7c14feb5db..025b31aa71 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -483,11 +483,12 @@ static int __intel_dsm(struct intel_host *intel_host, struct device *dev, int err = 0; size_t len; - obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL); + obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL, + ACPI_TYPE_BUFFER); if (!obj) return -EOPNOTSUPP; - if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1) { + if (obj->buffer.length < 1) { err = -EINVAL; goto out; } diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 7bfee28116..d4a0218478 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -693,6 +693,35 @@ static int sdhci_pci_o2_init_sd_express(struct mmc_host *mmc, struct mmc_ios *io return 0; } +static void sdhci_pci_o2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) +{ + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot = sdhci_priv(host); + u32 scratch_32 = 0; + u8 scratch_8 = 0; + + chip = slot->chip; + + if (mode == MMC_POWER_OFF) { + /* UnLock WP */ + pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); + scratch_8 &= 0x7f; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); + + /* Set PCR 0x354[16] to switch Clock Source back to OPE Clock */ + pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, &scratch_32); + scratch_32 &= ~(O2_SD_SEL_DLL); + pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, scratch_32); + + /* Lock WP */ + pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); + scratch_8 |= 0x80; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); + } + + sdhci_set_power(host, mode, vdd); +} + static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) { struct sdhci_pci_chip *chip; @@ -1051,6 +1080,7 @@ static const struct sdhci_ops sdhci_pci_o2_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_power = sdhci_pci_o2_set_power, }; const struct sdhci_pci_fixes sdhci_o2 = { diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index a72e123a58..62753d7219 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -19,7 +19,6 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/property.h> -#include <linux/of.h> #ifdef CONFIG_PPC #include <asm/machdep.h> #endif @@ -56,19 +55,16 @@ static bool sdhci_wp_inverted(struct device *dev) static void sdhci_get_compatibility(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct sdhci_host *host = platform_get_drvdata(pdev); - struct device_node *np = pdev->dev.of_node; - - if (!np) - return; - if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) + if (device_is_compatible(dev, "fsl,p2020-rev1-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_DMA; - if (of_device_is_compatible(np, "fsl,p2020-esdhc") || - of_device_is_compatible(np, "fsl,p1010-esdhc") || - of_device_is_compatible(np, "fsl,t4240-esdhc") || - of_device_is_compatible(np, "fsl,mpc8536-esdhc")) + if (device_is_compatible(dev, "fsl,p2020-esdhc") || + device_is_compatible(dev, "fsl,p1010-esdhc") || + device_is_compatible(dev, "fsl,t4240-esdhc") || + device_is_compatible(dev, "fsl,mpc8536-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; } @@ -115,26 +111,21 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, { struct sdhci_host *host; void __iomem *ioaddr; - int irq, ret; + int irq; ioaddr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ioaddr)) { - ret = PTR_ERR(ioaddr); - goto err; - } + if (IS_ERR(ioaddr)) + return ERR_CAST(ioaddr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto err; - } + if (irq < 0) + return ERR_PTR(irq); host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pltfm_host) + priv_size); - if (IS_ERR(host)) { - ret = PTR_ERR(host); - goto err; + dev_err(&pdev->dev, "%s failed %pe\n", __func__, host); + return ERR_CAST(host); } host->ioaddr = ioaddr; @@ -152,9 +143,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, platform_set_drvdata(pdev, host); return host; -err: - dev_err(&pdev->dev, "%s failed %d\n", __func__, ret); - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(sdhci_pltfm_init); diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index cef0e716ad..fd67c0682b 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -512,7 +512,7 @@ static void new_system_port_status(struct vub300_mmc_host *vub300) vub300->card_present = 1; vub300->bus_width = 0; if (disable_offload_processing) - strncpy(vub300->vub_name, "EMPTY Processing Disabled", + strscpy(vub300->vub_name, "EMPTY Processing Disabled", sizeof(vub300->vub_name)); else vub300->vub_name[0] = 0; @@ -1216,7 +1216,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, dev_err(&vub300->udev->dev, "corrupt offload pseudocode in firmware %s\n", vub300->vub_name); - strncpy(vub300->vub_name, "corrupt offload pseudocode", + strscpy(vub300->vub_name, "corrupt offload pseudocode", sizeof(vub300->vub_name)); return; } @@ -1250,7 +1250,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, "not enough memory for xfer buffer to send" " INTERRUPT_PSEUDOCODE for %s %s\n", fw->data, vub300->vub_name); - strncpy(vub300->vub_name, + strscpy(vub300->vub_name, "SDIO interrupt pseudocode download failed", sizeof(vub300->vub_name)); return; @@ -1259,7 +1259,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, dev_err(&vub300->udev->dev, "corrupt interrupt pseudocode in firmware %s %s\n", fw->data, vub300->vub_name); - strncpy(vub300->vub_name, "corrupt interrupt pseudocode", + strscpy(vub300->vub_name, "corrupt interrupt pseudocode", sizeof(vub300->vub_name)); return; } @@ -1293,7 +1293,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, "not enough memory for xfer buffer to send" " TRANSFER_PSEUDOCODE for %s %s\n", fw->data, vub300->vub_name); - strncpy(vub300->vub_name, + strscpy(vub300->vub_name, "SDIO transfer pseudocode download failed", sizeof(vub300->vub_name)); return; @@ -1302,7 +1302,7 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, dev_err(&vub300->udev->dev, "corrupt transfer pseudocode in firmware %s %s\n", fw->data, vub300->vub_name); - strncpy(vub300->vub_name, "corrupt transfer pseudocode", + strscpy(vub300->vub_name, "corrupt transfer pseudocode", sizeof(vub300->vub_name)); return; } @@ -1336,13 +1336,13 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, dev_err(&vub300->udev->dev, "corrupt dynamic registers in firmware %s\n", vub300->vub_name); - strncpy(vub300->vub_name, "corrupt dynamic registers", + strscpy(vub300->vub_name, "corrupt dynamic registers", sizeof(vub300->vub_name)); return; } copy_error_message: - strncpy(vub300->vub_name, "SDIO pseudocode download failed", + strscpy(vub300->vub_name, "SDIO pseudocode download failed", sizeof(vub300->vub_name)); } @@ -1370,11 +1370,11 @@ static void download_offload_pseudocode(struct vub300_mmc_host *vub300) vub300->vub_name); retval = request_firmware(&fw, vub300->vub_name, &card->dev); if (retval < 0) { - strncpy(vub300->vub_name, "vub_default.bin", + strscpy(vub300->vub_name, "vub_default.bin", sizeof(vub300->vub_name)); retval = request_firmware(&fw, vub300->vub_name, &card->dev); if (retval < 0) { - strncpy(vub300->vub_name, + strscpy(vub300->vub_name, "no SDIO offload firmware found", sizeof(vub300->vub_name)); } else { @@ -1758,7 +1758,7 @@ static void vub300_cmndwork_thread(struct work_struct *work) * has been already downloaded to the VUB300 chip */ } else if (0 == vub300->mmc->card->sdio_funcs) { - strncpy(vub300->vub_name, "SD memory device", + strscpy(vub300->vub_name, "SD memory device", sizeof(vub300->vub_name)); } else { download_offload_pseudocode(vub300); |