summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-15 14:15:25 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-15 14:15:25 -0700
commit4ddd4f0651a710f33dfbb9dadd94f2bb0aa31aa8 (patch)
treee1d5912e626588751347c425a54a3936fdf3f6e1 /drivers
parentb9962335d4c6dee152e95dce9f0dd32048735a6d (diff)
parent52957cdad30f8011da1f4ef1338ba0339ca4c158 (diff)
downloadlwn-4ddd4f0651a710f33dfbb9dadd94f2bb0aa31aa8.tar.gz
lwn-4ddd4f0651a710f33dfbb9dadd94f2bb0aa31aa8.zip
Merge tag 'mmc-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Add NXP vendor and IW61x device IDs for WiFi chips over SDIO - Add quirk for incorrect manufacturing date - Add support for manufacturing date beyond 2025 - Optimize support for secure erase/trim for some Kingston eMMCs - Remove support for the legacy "enable-sdio-wakeup" DT property - Use single block writes in the retry path MMC host: - dw_mmc: - A great amount of cleanups/simplifications to improve the code - Add clk_phase_map support - Remove mshc DT alias support - dw_mmc-rockchip: - Fix runtime PM support for internal phase - Add support for the RV1103B variant - loongson2: - Add support for the Loongson-2K0300 SD/SDIO/eMMC controller - mtk-sd: - Add support for the MT8189 variant - renesas_sdhi_core: - Add support for selecting an optional mux - rtsx_pci_sdmmc: - Simplify voltage switch handling - sdhci: - Stop advertising the driver in dmesg - sdhci-esdhc-imx: - Add 1-bit bus width support - Add support for the NXP S32N79 variant - sdhci-msm: - Add support for the IPQ5210 and IPQ9650 variants - Add support for wrapped keys - Enable ICE for CQE-capable controllers with non-CQE cards - sdhci-of-arasan: - Add support for the Axiado AX3000 variant - sdhci-of-aspeed: - Add support for the AST2700 variant - sdhci-of-bst: - Add driver for the Black Sesame Technologies C1200 controller - sdhci-of-dwcmshc: - Add support for the Canaan K230 variant - Add support for the HPE GSC variant - Prevent clock glitches to avoid malfunction - sdhci-of-k1: - Add support for the K3 variant mux core/consumers: - core: - Add helper functions for getting optional and selected mux-state - i2c-omap: - Convert to devm_mux_state_get_optional_selected() - phy-renesas: - Convert to devm_mux_state_get_optional_selected() - phy-can-transceiver: - Convert to devm_mux_state_get_optional()" * tag 'mmc-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (131 commits) mmc: sdhci-msm: Fix the wrapped key handling mmc: sdhci-of-dwcmshc: Disable clock before DLL configuration mmc: core: Simplify with scoped for each OF child loop mmc: core: Optimize size of struct mmc_queue_req mmc: vub300: clean up module init mmc: vub300: rename probe error labels mmc: dw_mmc: Remove dw_mci_start_request wrapper and rename core function mmc: dw_mmc: Inline dw_mci_queue_request() into dw_mci_request() mmc: block: Use MQRQ_XFER_SINGLE_BLOCK for both read and write recovery mmc: mmc_test: Replace hard-coded values with macros and consolidate test parameters mmc: block: Convert to use DEFINE_SIMPLE_DEV_PM_OPS() mmc: core: Replace the hard-coded shift value 9 with SECTOR_SHIFT mmc: sdhci-dwcmshc: Refactor Rockchip platform data for controller revisions mmc: core: Switch to use pm_ptr() for mmc_host_class_dev_pm_ops mmc: core: Remove legacy 'enable-sdio-wakeup' DT property support mmc: mmc_test: use kzalloc_flex mmc: mtk-sd: disable new_tx/rx and modify related settings for mt8189 dt-bindings: mmc: hisilicon,hi3660-dw-mshc: Convert to DT schema dt-bindings: mmc: sdhci-msm: add IPQ9650 compatible mmc: block: use single block write in retry ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/busses/i2c-omap.c24
-rw-r--r--drivers/mmc/core/block.c86
-rw-r--r--drivers/mmc/core/card.h11
-rw-r--r--drivers/mmc/core/core.c3
-rw-r--r--drivers/mmc/core/host.c29
-rw-r--r--drivers/mmc/core/host.h6
-rw-r--r--drivers/mmc/core/mmc.c12
-rw-r--r--drivers/mmc/core/mmc_test.c111
-rw-r--r--drivers/mmc/core/queue.c9
-rw-r--r--drivers/mmc/core/queue.h7
-rw-r--r--drivers/mmc/core/quirks.h21
-rw-r--r--drivers/mmc/core/sdio_io.c6
-rw-r--r--drivers/mmc/host/Kconfig16
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/atmel-mci.c12
-rw-r--r--drivers/mmc/host/cavium-octeon.c5
-rw-r--r--drivers/mmc/host/cavium.c4
-rw-r--r--drivers/mmc/host/dw_mmc-bluefield.c2
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c9
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798cv200.c6
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798mv200.c28
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c51
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c37
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c34
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.h2
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c36
-rw-r--r--drivers/mmc/host/dw_mmc-starfive.c5
-rw-r--r--drivers/mmc/host/dw_mmc.c819
-rw-r--r--drivers/mmc/host/dw_mmc.h148
-rw-r--r--drivers/mmc/host/jz4740_mmc.c2
-rw-r--r--drivers/mmc/host/loongson2-mmc.c60
-rw-r--r--drivers/mmc/host/mtk-sd.c32
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c6
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c12
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c88
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c33
-rw-r--r--drivers/mmc/host/sdhci-msm.c124
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c88
-rw-r--r--drivers/mmc/host/sdhci-of-aspeed.c11
-rw-r--r--drivers/mmc/host/sdhci-of-bst.c523
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c517
-rw-r--r--drivers/mmc/host/sdhci-of-k1.c39
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c1
-rw-r--r--drivers/mmc/host/sdhci-pic32.c5
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c20
-rw-r--r--drivers/mmc/host/sdhci-uhs2.c13
-rw-r--r--drivers/mmc/host/sdhci.c23
-rw-r--r--drivers/mmc/host/tifm_sd.c4
-rw-r--r--drivers/mmc/host/vub300.c51
-rw-r--r--drivers/mux/Kconfig17
-rw-r--r--drivers/mux/core.c194
-rw-r--r--drivers/phy/phy-can-transceiver.c10
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-usb2.c30
53 files changed, 2121 insertions, 1322 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index d9f590f0c384..f02d294db42a 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev)
(1000 * omap->speed / 8);
}
- if (of_property_present(node, "mux-states")) {
- struct mux_state *mux_state;
-
- mux_state = devm_mux_state_get(&pdev->dev, NULL);
- if (IS_ERR(mux_state)) {
- r = PTR_ERR(mux_state);
- dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r);
- goto err_put_pm;
- }
- omap->mux_state = mux_state;
- r = mux_state_select(omap->mux_state);
- if (r) {
- dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r);
- goto err_put_pm;
- }
+ omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
+ if (IS_ERR(omap->mux_state)) {
+ r = PTR_ERR(omap->mux_state);
+ goto err_put_pm;
}
/* reset ASAP, clearing any IRQs */
r = omap_i2c_init(omap);
if (r)
- goto err_mux_state_deselect;
+ goto err_put_pm;
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
@@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev)
err_unuse_clocks:
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
-err_mux_state_deselect:
- if (omap->mux_state)
- mux_state_deselect(omap->mux_state);
err_put_pm:
pm_runtime_put_sync(omap->dev);
err_disable_pm:
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 05ee76cb0a08..0274e8d07660 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -1401,6 +1401,9 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
rq_data_dir(req) == WRITE &&
(md->flags & MMC_BLK_REL_WR);
+ if (mqrq->flags & MQRQ_XFER_SINGLE_BLOCK)
+ recovery_mode = 1;
+
memset(brq, 0, sizeof(struct mmc_blk_request));
mmc_crypto_prepare_req(mqrq);
@@ -1455,7 +1458,7 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
* sectors can be read successfully.
*/
if (recovery_mode)
- brq->data.blocks = queue_physical_block_size(mq->queue) >> 9;
+ brq->data.blocks = queue_physical_block_size(mq->queue) >> SECTOR_SHIFT;
/*
* Some controllers have HW issues while operating
@@ -1540,10 +1543,12 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req)
err = 0;
if (err) {
- if (mqrq->retries++ < MMC_CQE_RETRIES)
+ if (mqrq->retries++ < MMC_CQE_RETRIES) {
+ mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK;
blk_mq_requeue_request(req, true);
- else
+ } else {
blk_mq_end_request(req, BLK_STS_IOERR);
+ }
} else if (mrq->data) {
if (blk_update_request(req, BLK_STS_OK, mrq->data->bytes_xfered))
blk_mq_requeue_request(req, true);
@@ -1776,63 +1781,6 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req)
return err;
}
-#define MMC_READ_SINGLE_RETRIES 2
-
-/* Single (native) sector read during recovery */
-static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
-{
- struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
- struct mmc_request *mrq = &mqrq->brq.mrq;
- struct mmc_card *card = mq->card;
- struct mmc_host *host = card->host;
- blk_status_t error = BLK_STS_OK;
- size_t bytes_per_read = queue_physical_block_size(mq->queue);
-
- do {
- u32 status;
- int err;
- int retries = 0;
-
- while (retries++ <= MMC_READ_SINGLE_RETRIES) {
- mmc_blk_rw_rq_prep(mqrq, card, 1, mq);
-
- mmc_wait_for_req(host, mrq);
-
- err = mmc_send_status(card, &status);
- if (err)
- goto error_exit;
-
- if (!mmc_host_is_spi(host) &&
- !mmc_ready_for_data(status)) {
- err = mmc_blk_fix_state(card, req);
- if (err)
- goto error_exit;
- }
-
- if (!mrq->cmd->error)
- break;
- }
-
- if (mrq->cmd->error ||
- mrq->data->error ||
- (!mmc_host_is_spi(host) &&
- (mrq->cmd->resp[0] & CMD_ERRORS || status & CMD_ERRORS)))
- error = BLK_STS_IOERR;
- else
- error = BLK_STS_OK;
-
- } while (blk_update_request(req, error, bytes_per_read));
-
- return;
-
-error_exit:
- mrq->data->bytes_xfered = 0;
- blk_update_request(req, BLK_STS_IOERR, bytes_per_read);
- /* Let it try the remaining request again */
- if (mqrq->retries > MMC_MAX_RETRIES - 1)
- mqrq->retries = MMC_MAX_RETRIES - 1;
-}
-
static inline bool mmc_blk_oor_valid(struct mmc_blk_request *brq)
{
return !!brq->mrq.sbc;
@@ -1968,13 +1916,6 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req)
mqrq->retries = MMC_MAX_RETRIES - MMC_DATA_RETRIES;
return;
}
-
- if (rq_data_dir(req) == READ && brq->data.blocks >
- queue_physical_block_size(mq->queue) >> 9) {
- /* Read one (native) sector at a time */
- mmc_blk_read_single(mq, req);
- return;
- }
}
static inline bool mmc_blk_rq_error(struct mmc_blk_request *brq)
@@ -2085,6 +2026,7 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req)
} else if (!blk_rq_bytes(req)) {
__blk_mq_end_request(req, BLK_STS_IOERR);
} else if (mqrq->retries++ < MMC_MAX_RETRIES) {
+ mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK;
blk_mq_requeue_request(req, true);
} else {
if (mmc_card_removed(mq->card))
@@ -3017,14 +2959,14 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
*/
ret = mmc_blk_alloc_rpmb_part(card, md,
card->part[idx].part_cfg,
- card->part[idx].size >> 9,
+ card->part[idx].size >> SECTOR_SHIFT,
card->part[idx].name);
if (ret)
return ret;
} else if (card->part[idx].size) {
ret = mmc_blk_alloc_part(card, md,
card->part[idx].part_cfg,
- card->part[idx].size >> 9,
+ card->part[idx].size >> SECTOR_SHIFT,
card->part[idx].force_ro,
card->part[idx].name,
card->part[idx].area_type);
@@ -3354,7 +3296,6 @@ static void mmc_blk_shutdown(struct mmc_card *card)
_mmc_blk_suspend(card);
}
-#ifdef CONFIG_PM_SLEEP
static int mmc_blk_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
@@ -3380,14 +3321,13 @@ static int mmc_blk_resume(struct device *dev)
}
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
- .pm = &mmc_blk_pm_ops,
+ .pm = pm_sleep_ptr(&mmc_blk_pm_ops),
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h
index 1200951bab08..a7c364d0030a 100644
--- a/drivers/mmc/core/card.h
+++ b/drivers/mmc/core/card.h
@@ -89,6 +89,7 @@ struct mmc_fixup {
#define CID_MANFID_MICRON 0x13
#define CID_MANFID_SAMSUNG 0x15
#define CID_MANFID_APACER 0x27
+#define CID_MANFID_SANDISK_MMC 0x45
#define CID_MANFID_SWISSBIT 0x5D
#define CID_MANFID_KINGSTON 0x70
#define CID_MANFID_HYNIX 0x90
@@ -305,4 +306,14 @@ static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING;
}
+static inline int mmc_card_broken_mdt(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_BROKEN_MDT;
+}
+
+static inline int mmc_card_fixed_secure_erase_trim_time(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME;
+}
+
#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 860378bea557..29e80e5f928e 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -97,7 +97,8 @@ static void mmc_should_fail_request(struct mmc_host *host,
return;
data->error = data_errors[get_random_u32_below(ARRAY_SIZE(data_errors))];
- data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> 9) << 9;
+ data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> SECTOR_SHIFT)
+ << SECTOR_SHIFT;
}
#else /* CONFIG_FAIL_MMC_REQUEST */
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 88c95dbfd9cf..b7ce3137d452 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -33,7 +33,6 @@
static DEFINE_IDA(mmc_host_ida);
-#ifdef CONFIG_PM_SLEEP
static int mmc_host_class_prepare(struct device *dev)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
@@ -60,15 +59,10 @@ static void mmc_host_class_complete(struct device *dev)
}
static const struct dev_pm_ops mmc_host_class_dev_pm_ops = {
- .prepare = mmc_host_class_prepare,
- .complete = mmc_host_class_complete,
+ .prepare = pm_sleep_ptr(mmc_host_class_prepare),
+ .complete = pm_sleep_ptr(mmc_host_class_complete),
};
-#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops)
-#else
-#define MMC_HOST_CLASS_DEV_PM_OPS NULL
-#endif
-
static void mmc_host_classdev_release(struct device *dev)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
@@ -90,7 +84,7 @@ static const struct class mmc_host_class = {
.name = "mmc_host",
.dev_release = mmc_host_classdev_release,
.shutdown_pre = mmc_host_classdev_shutdown,
- .pm = MMC_HOST_CLASS_DEV_PM_OPS,
+ .pm = pm_ptr(&mmc_host_class_dev_pm_ops),
};
int mmc_register_host_class(void)
@@ -379,8 +373,7 @@ int mmc_of_parse(struct mmc_host *host)
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND;
if (device_property_read_bool(dev, "keep-power-in-suspend"))
host->pm_caps |= MMC_PM_KEEP_POWER;
- if (device_property_read_bool(dev, "wakeup-source") ||
- device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
+ if (device_property_read_bool(dev, "wakeup-source"))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
if (device_property_read_bool(dev, "mmc-ddr-3_3v"))
host->caps |= MMC_CAP_3_3V_DDR;
@@ -624,12 +617,24 @@ static int mmc_validate_host_caps(struct mmc_host *host)
return -EINVAL;
}
+ /* UHS/DDR/HS200 modes require at least 4-bit bus */
+ if (!(caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) &&
+ ((caps & (MMC_CAP_UHS | MMC_CAP_DDR)) || (caps2 & MMC_CAP2_HS200))) {
+ dev_warn(dev, "drop UHS/DDR/HS200 support since 1-bit bus only\n");
+ caps &= ~(MMC_CAP_UHS | MMC_CAP_DDR);
+ caps2 &= ~MMC_CAP2_HS200;
+ }
+
+ /* HS400 and HS400ES modes require 8-bit bus */
if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) &&
!(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) {
dev_warn(dev, "drop HS400 support since no 8-bit bus\n");
- host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400;
+ caps2 &= ~(MMC_CAP2_HS400_ES | MMC_CAP2_HS400);
}
+ host->caps = caps;
+ host->caps2 = caps2;
+
return 0;
}
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 5941d68ff989..6bce5a4798e2 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -56,11 +56,7 @@ static inline int mmc_host_can_access_boot(struct mmc_host *host)
static inline int mmc_host_can_uhs(struct mmc_host *host)
{
- return host->caps &
- (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
- MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
- MMC_CAP_UHS_DDR50) &&
- host->caps & MMC_CAP_4_BIT_DATA;
+ return host->caps & MMC_CAP_UHS;
}
static inline bool mmc_card_hs200(struct mmc_card *card)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 7c86efb1044a..8846550a8892 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -671,7 +671,19 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.enhanced_rpmb_supported =
(card->ext_csd.rel_param &
EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR);
+
+ if (card->ext_csd.rev >= 9) {
+ /* Adjust production date as per JEDEC JESD84-B51B September 2025 */
+ if (card->cid.year < 2023)
+ card->cid.year += 16;
+ } else {
+ /* Handle vendors with broken MDT reporting */
+ if (mmc_card_broken_mdt(card) && card->cid.year >= 2010 &&
+ card->cid.year <= 2012)
+ card->cid.year += 16;
+ }
}
+
out:
return err;
}
diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c
index 43fdb67d5407..ab38e4c45a8d 100644
--- a/drivers/mmc/core/mmc_test.c
+++ b/drivers/mmc/core/mmc_test.c
@@ -37,7 +37,7 @@
* Limit the test area size to the maximum MMC HC erase group size. Note that
* the maximum SD allocation unit size is just 4MiB.
*/
-#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024)
+#define TEST_AREA_MAX_SIZE SZ_128M
/**
* struct mmc_test_pages - pages allocated by 'alloc_pages()'.
@@ -51,12 +51,12 @@ struct mmc_test_pages {
/**
* struct mmc_test_mem - allocated memory.
- * @arr: array of allocations
* @cnt: number of allocations
+ * @arr: array of allocations
*/
struct mmc_test_mem {
- struct mmc_test_pages *arr;
unsigned int cnt;
+ struct mmc_test_pages arr[] __counted_by(cnt);
};
/**
@@ -135,21 +135,22 @@ struct mmc_test_dbgfs_file {
* struct mmc_test_card - test information.
* @card: card under test
* @scratch: transfer buffer
- * @buffer: transfer buffer
* @highmem: buffer for highmem tests
* @area: information for performance tests
* @gr: pointer to results of current testcase
+ * @buffer: transfer buffer
*/
struct mmc_test_card {
struct mmc_card *card;
u8 scratch[BUFFER_SIZE];
- u8 *buffer;
#ifdef CONFIG_HIGHMEM
struct page *highmem;
#endif
struct mmc_test_area area;
struct mmc_test_general_result *gr;
+
+ u8 buffer[];
};
enum mmc_test_prep_media {
@@ -168,6 +169,11 @@ struct mmc_test_multiple_rw {
enum mmc_test_prep_media prepare;
};
+static unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
+ 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
+
+static unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
+ 1 << 7, 1 << 8, 1 << 9};
/*******************************************************************/
/* General helper functions */
/*******************************************************************/
@@ -315,7 +321,6 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem)
while (mem->cnt--)
__free_pages(mem->arr[mem->cnt].page,
mem->arr[mem->cnt].order);
- kfree(mem->arr);
kfree(mem);
}
@@ -348,14 +353,10 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
if (max_segs > max_page_cnt)
max_segs = max_page_cnt;
- mem = kzalloc_obj(*mem);
+ mem = kzalloc_flex(*mem, arr, max_segs);
if (!mem)
return NULL;
- mem->arr = kzalloc_objs(*mem->arr, max_segs);
- if (!mem->arr)
- goto out_free;
-
while (max_page_cnt) {
struct page *page;
unsigned int order;
@@ -506,7 +507,7 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec64 *ts)
uint64_t ns;
ns = timespec64_to_ns(ts);
- bytes *= 1000000000;
+ bytes *= NSEC_PER_SEC;
while (ns > UINT_MAX) {
bytes >>= 1;
@@ -552,7 +553,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test,
static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
struct timespec64 *ts1, struct timespec64 *ts2)
{
- unsigned int rate, iops, sectors = bytes >> 9;
+ unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT;
struct timespec64 ts;
ts = timespec64_sub(*ts2, *ts1);
@@ -577,7 +578,7 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
unsigned int count, struct timespec64 *ts1,
struct timespec64 *ts2)
{
- unsigned int rate, iops, sectors = bytes >> 9;
+ unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT;
uint64_t tot = bytes * count;
struct timespec64 ts;
@@ -1378,7 +1379,7 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
int err;
unsigned int sg_len = 0;
- t->blocks = sz >> 9;
+ t->blocks = sz >> SECTOR_SHIFT;
if (max_scatter) {
err = mmc_test_map_sg_max_scatter(t->mem, sz, t->sg,
@@ -1461,7 +1462,7 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz,
else
for (i = 0; i < count && ret == 0; i++) {
ret = mmc_test_area_transfer(test, dev_addr, write);
- dev_addr += sz >> 9;
+ dev_addr += sz >> SECTOR_SHIFT;
}
if (ret)
@@ -1504,7 +1505,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test)
if (!mmc_card_can_erase(test->card))
return 0;
- return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9,
+ return mmc_erase(test->card, t->dev_addr, t->max_sz >> SECTOR_SHIFT,
MMC_ERASE_ARG);
}
@@ -1532,7 +1533,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test)
static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
{
struct mmc_test_area *t = &test->area;
- unsigned long min_sz = 64 * 1024, sz;
+ unsigned long min_sz = SZ_64K, sz;
int ret;
ret = mmc_test_set_blksize(test, 512);
@@ -1540,9 +1541,9 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
return ret;
/* Make the test area size about 4MiB */
- sz = (unsigned long)test->card->pref_erase << 9;
+ sz = (unsigned long)test->card->pref_erase << SECTOR_SHIFT;
t->max_sz = sz;
- while (t->max_sz < 4 * 1024 * 1024)
+ while (t->max_sz < SZ_4M)
t->max_sz += sz;
while (t->max_sz > TEST_AREA_MAX_SIZE && t->max_sz > sz)
t->max_sz -= sz;
@@ -1552,8 +1553,8 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
t->max_seg_sz -= t->max_seg_sz % 512;
t->max_tfr = t->max_sz;
- if (t->max_tfr >> 9 > test->card->host->max_blk_count)
- t->max_tfr = test->card->host->max_blk_count << 9;
+ if (t->max_tfr >> SECTOR_SHIFT > test->card->host->max_blk_count)
+ t->max_tfr = test->card->host->max_blk_count << SECTOR_SHIFT;
if (t->max_tfr > test->card->host->max_req_size)
t->max_tfr = test->card->host->max_req_size;
if (t->max_tfr / t->max_seg_sz > t->max_segs)
@@ -1583,7 +1584,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
}
t->dev_addr = mmc_test_capacity(test->card) / 2;
- t->dev_addr -= t->dev_addr % (t->max_sz >> 9);
+ t->dev_addr -= t->dev_addr % (t->max_sz >> SECTOR_SHIFT);
if (erase) {
ret = mmc_test_area_erase(test);
@@ -1688,7 +1689,7 @@ static int mmc_test_profile_read_perf(struct mmc_test_card *test)
int ret;
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
- dev_addr = t->dev_addr + (sz >> 9);
+ dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT);
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
if (ret)
return ret;
@@ -1712,7 +1713,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
if (ret)
return ret;
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
- dev_addr = t->dev_addr + (sz >> 9);
+ dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT);
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
if (ret)
return ret;
@@ -1743,9 +1744,9 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
return RESULT_UNSUP_HOST;
for (sz = 512; sz < t->max_sz; sz <<= 1) {
- dev_addr = t->dev_addr + (sz >> 9);
+ dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT);
ktime_get_ts64(&ts1);
- ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
+ ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG);
if (ret)
return ret;
ktime_get_ts64(&ts2);
@@ -1753,7 +1754,7 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
}
dev_addr = t->dev_addr;
ktime_get_ts64(&ts1);
- ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
+ ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG);
if (ret)
return ret;
ktime_get_ts64(&ts2);
@@ -1775,7 +1776,7 @@ static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz)
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
if (ret)
return ret;
- dev_addr += (sz >> 9);
+ dev_addr += (sz >> SECTOR_SHIFT);
}
ktime_get_ts64(&ts2);
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
@@ -1817,7 +1818,7 @@ static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
if (ret)
return ret;
- dev_addr += (sz >> 9);
+ dev_addr += (sz >> SECTOR_SHIFT);
}
ktime_get_ts64(&ts2);
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
@@ -1870,11 +1871,11 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
dev_addr = t->dev_addr;
ktime_get_ts64(&ts1);
for (i = 0; i < cnt; i++) {
- ret = mmc_erase(test->card, dev_addr, sz >> 9,
+ ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT,
MMC_TRIM_ARG);
if (ret)
return ret;
- dev_addr += (sz >> 9);
+ dev_addr += (sz >> SECTOR_SHIFT);
}
ktime_get_ts64(&ts2);
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
@@ -1901,7 +1902,7 @@ static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print,
struct timespec64 ts1, ts2, ts;
int ret;
- ssz = sz >> 9;
+ ssz = sz >> SECTOR_SHIFT;
rnd_addr = mmc_test_capacity(test->card) / 4;
range1 = rnd_addr / test->card->pref_erase;
@@ -2017,10 +2018,10 @@ static int mmc_test_seq_perf(struct mmc_test_card *test, int write,
sz = max_tfr;
}
- ssz = sz >> 9;
+ ssz = sz >> SECTOR_SHIFT;
dev_addr = mmc_test_capacity(test->card) / 4;
- if (tot_sz > dev_addr << 9)
- tot_sz = dev_addr << 9;
+ if (tot_sz > dev_addr << SECTOR_SHIFT)
+ tot_sz = dev_addr << SECTOR_SHIFT;
cnt = tot_sz / sz;
dev_addr &= 0xffff0000; /* Round to 64MiB boundary */
@@ -2044,17 +2045,17 @@ static int mmc_test_large_seq_perf(struct mmc_test_card *test, int write)
int ret, i;
for (i = 0; i < 10; i++) {
- ret = mmc_test_seq_perf(test, write, 10 * 1024 * 1024, 1);
+ ret = mmc_test_seq_perf(test, write, 10 * SZ_1M, 1);
if (ret)
return ret;
}
for (i = 0; i < 5; i++) {
- ret = mmc_test_seq_perf(test, write, 100 * 1024 * 1024, 1);
+ ret = mmc_test_seq_perf(test, write, 100 * SZ_1M, 1);
if (ret)
return ret;
}
for (i = 0; i < 3; i++) {
- ret = mmc_test_seq_perf(test, write, 1000 * 1024 * 1024, 1);
+ ret = mmc_test_seq_perf(test, write, 1000 * SZ_1M, 1);
if (ret)
return ret;
}
@@ -2157,7 +2158,7 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
int i;
for (i = 0 ; i < rw->len && ret == 0; i++) {
- ret = mmc_test_rw_multiple(test, rw, 512 * 1024, rw->size,
+ ret = mmc_test_rw_multiple(test, rw, SZ_512K, rw->size,
rw->sg_len[i]);
if (ret)
break;
@@ -2170,8 +2171,6 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
*/
static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
{
- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
struct mmc_test_multiple_rw test_data = {
.bs = bs,
.size = TEST_AREA_MAX_SIZE,
@@ -2189,8 +2188,6 @@ static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
{
- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
struct mmc_test_multiple_rw test_data = {
.bs = bs,
.size = TEST_AREA_MAX_SIZE,
@@ -2208,8 +2205,6 @@ static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
{
- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
struct mmc_test_multiple_rw test_data = {
.bs = bs,
.size = TEST_AREA_MAX_SIZE,
@@ -2227,8 +2222,6 @@ static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
{
- unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
- 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
struct mmc_test_multiple_rw test_data = {
.bs = bs,
.size = TEST_AREA_MAX_SIZE,
@@ -2246,8 +2239,6 @@ static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test)
{
- unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
- 1 << 7, 1 << 8, 1 << 9};
struct mmc_test_multiple_rw test_data = {
.sg_len = sg_len,
.size = TEST_AREA_MAX_SIZE,
@@ -2265,8 +2256,6 @@ static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test)
{
- unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
- 1 << 7, 1 << 8, 1 << 9};
struct mmc_test_multiple_rw test_data = {
.sg_len = sg_len,
.size = TEST_AREA_MAX_SIZE,
@@ -2284,8 +2273,6 @@ static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test)
{
- unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
- 1 << 7, 1 << 8, 1 << 9};
struct mmc_test_multiple_rw test_data = {
.sg_len = sg_len,
.size = TEST_AREA_MAX_SIZE,
@@ -2303,8 +2290,6 @@ static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test)
*/
static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test)
{
- unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
- 1 << 7, 1 << 8, 1 << 9};
struct mmc_test_multiple_rw test_data = {
.sg_len = sg_len,
.size = TEST_AREA_MAX_SIZE,
@@ -2456,7 +2441,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
if (ret)
goto out_free;
- if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr)
+ if (repeat_cmd && (t->blocks + 1) << SECTOR_SHIFT > t->max_tfr)
pr_info("%s: %d commands completed during transfer of %u blocks\n",
mmc_hostname(test->card->host), count, t->blocks);
@@ -3099,7 +3084,7 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
if (ret)
return ret;
- test = kzalloc_obj(*test);
+ test = kzalloc_flex(*test, buffer, BUFFER_SIZE);
if (!test)
return -ENOMEM;
@@ -3111,7 +3096,6 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
test->card = card;
- test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
#ifdef CONFIG_HIGHMEM
test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
if (!test->highmem) {
@@ -3120,17 +3104,14 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
}
#endif
- if (test->buffer) {
- mutex_lock(&mmc_test_lock);
- mmc_test_run(test, testcase);
- mutex_unlock(&mmc_test_lock);
- }
+ mutex_lock(&mmc_test_lock);
+ mmc_test_run(test, testcase);
+ mutex_unlock(&mmc_test_lock);
#ifdef CONFIG_HIGHMEM
__free_pages(test->highmem, BUFFER_ORDER);
free_test_buffer:
#endif
- kfree(test->buffer);
kfree(test);
return count;
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 13000fc57e2e..39fcb662c43f 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -184,8 +184,13 @@ static void mmc_queue_setup_discard(struct mmc_card *card,
return;
lim->max_hw_discard_sectors = max_discard;
- if (mmc_card_can_secure_erase_trim(card))
- lim->max_secure_erase_sectors = max_discard;
+ if (mmc_card_can_secure_erase_trim(card)) {
+ if (mmc_card_fixed_secure_erase_trim_time(card))
+ lim->max_secure_erase_sectors = UINT_MAX >> card->erase_shift;
+ else
+ lim->max_secure_erase_sectors = max_discard;
+ }
+
if (mmc_card_can_trim(card) && card->erased_byte == 0)
lim->max_write_zeroes_sectors = max_discard;
diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h
index 1498840a4ea0..dd7211e3a6d5 100644
--- a/drivers/mmc/core/queue.h
+++ b/drivers/mmc/core/queue.h
@@ -61,14 +61,17 @@ enum mmc_drv_op {
MMC_DRV_OP_GET_EXT_CSD,
};
+#define MQRQ_XFER_SINGLE_BLOCK BIT(0)
+
struct mmc_queue_req {
struct mmc_blk_request brq;
struct scatterlist *sg;
enum mmc_drv_op drv_op;
int drv_op_result;
void *drv_op_data;
- unsigned int ioc_count;
- int retries;
+ u8 ioc_count;
+ u8 retries;
+ u8 flags;
};
struct mmc_queue {
diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h
index c417ed34c057..940549d3b95d 100644
--- a/drivers/mmc/core/quirks.h
+++ b/drivers/mmc/core/quirks.h
@@ -153,6 +153,15 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = {
MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
MMC_QUIRK_TRIM_BROKEN),
+ /*
+ * On Some Kingston eMMCs, secure erase/trim time is independent
+ * of erase size, fixed at approximately 2 seconds.
+ */
+ MMC_FIXUP("IY2964", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
+ MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME),
+ MMC_FIXUP("IB2932", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
+ MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME),
+
END_FIXUP
};
@@ -170,6 +179,9 @@ static const struct mmc_fixup __maybe_unused mmc_ext_csd_fixups[] = {
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_NUMONYX,
0x014e, add_quirk, MMC_QUIRK_BROKEN_HPI, 6),
+ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_MMC, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BROKEN_MDT),
+
END_FIXUP
};
@@ -213,14 +225,9 @@ static const struct mmc_fixup __maybe_unused sdio_card_init_methods[] = {
static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card,
const char *compatible)
{
- struct device_node *np;
-
- for_each_child_of_node(mmc_dev(card->host)->of_node, np) {
- if (of_device_is_compatible(np, compatible)) {
- of_node_put(np);
+ for_each_child_of_node_scoped(mmc_dev(card->host)->of_node, np)
+ if (of_device_is_compatible(np, compatible))
return true;
- }
- }
return false;
}
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index b774bf51981d..12716ec0e35d 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -163,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
if (blksz > func->card->host->max_blk_size)
return -EINVAL;
- if (blksz == 0) {
- blksz = min(func->max_blksize, func->card->host->max_blk_size);
- blksz = min(blksz, 512u);
- }
+ if (blksz == 0)
+ blksz = min3(func->max_blksize, func->card->host->max_blk_size, 512u);
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 6d79cc9a79e2..4f060d3e5636 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -429,6 +429,20 @@ config MMC_SDHCI_BCM_KONA
If you have a controller with this interface, say Y or M here.
+config MMC_SDHCI_BST
+ tristate "SDHCI support for Black Sesame Technologies BST C1200 controller"
+ depends on ARCH_BST || COMPILE_TEST
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ for Black Sesame Technologies BST C1200 SoC. The controller is
+ based on Synopsys DesignWare Cores Mobile Storage Controller but
+ requires platform-specific workarounds for hardware limitations.
+
+ If you have a controller with this interface, say Y or M here.
+ If unsure, say N.
+
config MMC_SDHCI_F_SDH30
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
depends on MMC_SDHCI_PLTFM
@@ -1044,7 +1058,7 @@ config MMC_MTK
config MMC_SDHCI_MICROCHIP_PIC32
tristate "Microchip PIC32MZDA SDHCI support"
- depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM
+ depends on MMC_SDHCI && MMC_SDHCI_PLTFM && (PIC32MZDA || COMPILE_TEST)
help
This selects the Secure Digital Host Controller Interface (SDHCI)
for PIC32MZDA platform.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5057fea8afb6..ee412e6b84d6 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
+obj-$(CONFIG_MMC_SDHCI_BST) += sdhci-of-bst.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index fdf6926ea468..3b4928f5b9b2 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -629,14 +629,13 @@ static int atmci_of_init(struct atmel_mci *host)
{
struct device *dev = host->dev;
struct device_node *np = dev->of_node;
- struct device_node *cnp;
u32 slot_id;
int err;
if (!np)
return dev_err_probe(dev, -EINVAL, "device node not found\n");
- for_each_child_of_node(np, cnp) {
+ for_each_child_of_node_scoped(np, cnp) {
if (of_property_read_u32(cnp, "reg", &slot_id)) {
dev_warn(dev, "reg property is missing for %pOF\n", cnp);
continue;
@@ -645,7 +644,6 @@ static int atmci_of_init(struct atmel_mci *host)
if (slot_id >= ATMCI_MAX_NR_SLOTS) {
dev_warn(dev, "can't have more than %d slots\n",
ATMCI_MAX_NR_SLOTS);
- of_node_put(cnp);
break;
}
@@ -658,10 +656,8 @@ static int atmci_of_init(struct atmel_mci *host)
"cd", GPIOD_IN, "cd-gpios");
err = PTR_ERR_OR_ZERO(host->pdata[slot_id].detect_pin);
if (err) {
- if (err != -ENOENT) {
- of_node_put(cnp);
+ if (err != -ENOENT)
return err;
- }
host->pdata[slot_id].detect_pin = NULL;
}
@@ -673,10 +669,8 @@ static int atmci_of_init(struct atmel_mci *host)
"wp", GPIOD_IN, "wp-gpios");
err = PTR_ERR_OR_ZERO(host->pdata[slot_id].wp_pin);
if (err) {
- if (err != -ENOENT) {
- of_node_put(cnp);
+ if (err != -ENOENT)
return err;
- }
host->pdata[slot_id].wp_pin = NULL;
}
}
diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c
index 0592f356b1e5..8a0daddd9200 100644
--- a/drivers/mmc/host/cavium-octeon.c
+++ b/drivers/mmc/host/cavium-octeon.c
@@ -148,7 +148,7 @@ static void octeon_mmc_dmar_fixup_done(struct cvm_mmc_host *host)
static int octeon_mmc_probe(struct platform_device *pdev)
{
- struct device_node *cn, *node = pdev->dev.of_node;
+ struct device_node *node = pdev->dev.of_node;
struct cvm_mmc_host *host;
void __iomem *base;
int mmc_irq[9];
@@ -268,7 +268,7 @@ static int octeon_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
i = 0;
- for_each_child_of_node(node, cn) {
+ for_each_child_of_node_scoped(node, cn) {
host->slot_pdev[i] =
of_platform_device_create(cn, NULL, &pdev->dev);
if (!host->slot_pdev[i]) {
@@ -279,7 +279,6 @@ static int octeon_mmc_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Error populating slots\n");
octeon_mmc_set_shared_power(host, 0);
- of_node_put(cn);
goto error;
}
i++;
diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c
index 9a55db0e657c..37a88f2a0c86 100644
--- a/drivers/mmc/host/cavium.c
+++ b/drivers/mmc/host/cavium.c
@@ -905,9 +905,7 @@ static void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock)
{
struct mmc_host *mmc = slot->mmc;
- clock = min(clock, mmc->f_max);
- clock = max(clock, mmc->f_min);
- slot->clock = clock;
+ slot->clock = clamp(clock, mmc->f_min, mmc->f_max);
}
static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot)
diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c
index 3cf526ab0387..81dc072d0e3f 100644
--- a/drivers/mmc/host/dw_mmc-bluefield.c
+++ b/drivers/mmc/host/dw_mmc-bluefield.c
@@ -73,7 +73,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = {
.name = "dwmmc_bluefield",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_bluefield_match,
- .pm = &dw_mci_pltfm_pmops,
+ .pm = pm_ptr(&dw_mci_pmops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 384609671a9a..261344d3a8cf 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -185,8 +185,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
* HOLD register should be bypassed in case there is no phase shift
* applied on CMD/DATA that is sent to the card.
*/
- if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot)
- set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
+ if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel))
+ set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags);
}
static int dw_mci_exynos_runtime_resume(struct device *dev)
@@ -530,11 +530,10 @@ out:
return loc;
}
-static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+static int dw_mci_exynos_execute_tuning(struct dw_mci *host, u32 opcode)
{
- struct dw_mci *host = slot->host;
struct dw_mci_exynos_priv_data *priv = host->priv;
- struct mmc_host *mmc = slot->mmc;
+ struct mmc_host *mmc = host->mmc;
u8 start_smpl, smpl, candidates = 0;
s8 found;
int ret = 0;
diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c
index 0ccfae1b2dbe..4b723ed3259c 100644
--- a/drivers/mmc/host/dw_mmc-hi3798cv200.c
+++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c
@@ -57,11 +57,9 @@ static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
clk_set_phase(priv->drive_clk, 135);
}
-static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
- u32 opcode)
+static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci *host, u32 opcode)
{
static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
- struct dw_mci *host = slot->host;
struct hi3798cv200_priv *priv = host->priv;
int raise_point = -1, fall_point = -1;
int err, prev_err = -1;
@@ -72,7 +70,7 @@ static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
clk_set_phase(priv->sample_clk, degrees[i]);
mci_writel(host, RINTSTS, ALL_INT_CLR);
- err = mmc_send_tuning(slot->mmc, opcode, NULL);
+ err = mmc_send_tuning(host->mmc, opcode, NULL);
if (!err)
found = 1;
diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c
index 5791a975a944..fda417888be9 100644
--- a/drivers/mmc/host/dw_mmc-hi3798mv200.c
+++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c
@@ -30,13 +30,12 @@ struct dw_mci_hi3798mv200_priv {
struct clk *drive_clk;
struct regmap *crg_reg;
u32 sap_dll_offset;
- struct mmc_clk_phase_map phase_map;
};
static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
struct dw_mci_hi3798mv200_priv *priv = host->priv;
- struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing];
+ struct mmc_clk_phase phase = host->phase_map.phase[ios->timing];
u32 val;
val = mci_readl(host, ENABLE_SHIFT);
@@ -74,25 +73,24 @@ static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
}
}
-static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot)
+static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci *host)
{
- struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
+ struct dw_mci_hi3798mv200_priv *priv = host->priv;
return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
}
-static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot)
+static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci *host)
{
- struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
+ struct dw_mci_hi3798mv200_priv *priv = host->priv;
return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
}
-static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
+static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci *host,
u32 opcode)
{
static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
- struct dw_mci *host = slot->host;
struct dw_mci_hi3798mv200_priv *priv = host->priv;
int raise_point = -1, fall_point = -1, mid;
int err, prev_err = -1;
@@ -101,7 +99,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
int i;
int ret;
- ret = dw_mci_hi3798mv200_enable_tuning(slot);
+ ret = dw_mci_hi3798mv200_enable_tuning(host);
if (ret < 0)
return ret;
@@ -115,7 +113,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
*
* Treat edge(flip) found as an error too.
*/
- err = mmc_send_tuning(slot->mmc, opcode, NULL);
+ err = mmc_send_tuning(host->mmc, opcode, NULL);
regval = mci_readl(host, TUNING_CTRL);
if (err || (regval & SDMMC_TUNING_FIND_EDGE))
err = 1;
@@ -136,7 +134,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
}
tuning_out:
- ret = dw_mci_hi3798mv200_disable_tuning(slot);
+ ret = dw_mci_hi3798mv200_disable_tuning(host);
if (ret < 0)
return ret;
@@ -159,9 +157,9 @@ tuning_out:
* We don't care what timing we are tuning for,
* simply use the same phase for all timing needs tuning.
*/
- priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid];
- priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid];
- priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid];
+ host->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid];
+ host->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid];
+ host->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid];
clk_set_phase(priv->sample_clk, degrees[mid]);
dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
@@ -186,8 +184,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host)
if (!priv)
return -ENOMEM;
- mmc_of_parse_clk_phase(host->dev, &priv->phase_map);
-
priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample");
if (IS_ERR(priv->sample_clk))
return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk),
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index ad6aa1aea549..59b802c9f2b8 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -53,7 +53,6 @@
#define USE_DLY_MAX_SMPL (14)
struct k3_priv {
- int ctrl_id;
u32 cur_speed;
struct regmap *reg;
};
@@ -127,26 +126,17 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
if (IS_ERR(priv->reg))
priv->reg = NULL;
- priv->ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
- if (priv->ctrl_id < 0)
- priv->ctrl_id = 0;
-
- if (priv->ctrl_id >= TIMING_MODE)
- return -EINVAL;
-
host->priv = priv;
return 0;
}
-static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+static int dw_mci_hi6220_switch_voltage(struct dw_mci *host, struct mmc_ios *ios)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
struct k3_priv *priv;
- struct dw_mci *host;
+ struct mmc_host *mmc = host->mmc;
int min_uv, max_uv;
int ret;
- host = slot->host;
priv = host->priv;
if (!priv || !priv->reg)
@@ -199,7 +189,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
host->bus_hz = clk_get_rate(host->biu_clk);
}
-static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+static int dw_mci_hi6220_execute_tuning(struct dw_mci *host, u32 opcode)
{
return 0;
}
@@ -213,7 +203,7 @@ static const struct dw_mci_drv_data hi6220_data = {
.execute_tuning = dw_mci_hi6220_execute_tuning,
};
-static void dw_mci_hs_set_timing(struct dw_mci *host, int timing,
+static int dw_mci_hs_set_timing(struct dw_mci *host, int timing,
int smpl_phase)
{
u32 drv_phase;
@@ -222,10 +212,10 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing,
u32 enable_shift = 0;
u32 reg_value;
int ctrl_id;
- struct k3_priv *priv;
- priv = host->priv;
- ctrl_id = priv->ctrl_id;
+ ctrl_id = host->mmc->index;
+ if (ctrl_id >= TIMING_MODE)
+ return -EINVAL;
drv_phase = hs_timing_cfg[ctrl_id][timing].drv_phase;
smpl_dly = hs_timing_cfg[ctrl_id][timing].smpl_dly;
@@ -262,6 +252,8 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing,
/* We should delay 1ms wait for timing setting finished. */
usleep_range(1000, 2000);
+
+ return 0;
}
static int dw_mci_hi3660_init(struct dw_mci *host)
@@ -269,10 +261,9 @@ static int dw_mci_hi3660_init(struct dw_mci *host)
mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(SDCARD_RD_THRESHOLD,
SDMMC_CARD_RD_THR_EN));
- dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1);
host->bus_hz /= (GENCLK_DIV + 1);
- return 0;
+ return dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1);
}
static int dw_mci_set_sel18(struct dw_mci *host, bool set)
@@ -364,11 +355,10 @@ static int dw_mci_get_best_clksmpl(unsigned int sample_flag)
return middle_range;
}
-static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+static int dw_mci_hi3660_execute_tuning(struct dw_mci *host, u32 opcode)
{
int i = 0;
- struct dw_mci *host = slot->host;
- struct mmc_host *mmc = slot->mmc;
+ struct mmc_host *mmc = host->mmc;
int smpl_phase = 0;
u32 tuning_sample_flag = 0;
int best_clksmpl = 0;
@@ -398,21 +388,19 @@ static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
return 0;
}
-static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc,
+static int dw_mci_hi3660_switch_voltage(struct dw_mci *host,
struct mmc_ios *ios)
{
- int ret = 0;
- struct dw_mci_slot *slot = mmc_priv(mmc);
struct k3_priv *priv;
- struct dw_mci *host;
+ struct mmc_host *mmc = host->mmc;
+ int ret = 0;
- host = slot->host;
priv = host->priv;
if (!priv || !priv->reg)
return 0;
- if (priv->ctrl_id == DWMMC_SDIO_ID)
+ if (mmc->index == DWMMC_SDIO_ID)
return 0;
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
@@ -460,11 +448,6 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, drv_data);
}
-static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = {
- SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
- RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
-};
-
static struct platform_driver dw_mci_k3_pltfm_driver = {
.probe = dw_mci_k3_probe,
.remove = dw_mci_pltfm_remove,
@@ -472,7 +455,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = {
.name = "dwmmc_k3",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_k3_match,
- .pm = pm_ptr(&dw_mci_k3_dev_pm_ops),
+ .pm = pm_ptr(&dw_mci_pmops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index 092cc99175af..a2e1b4d75a5e 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -10,13 +10,14 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/pci.h>
+#include <linux/pci-epf.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
-#define PCI_BAR_NO 2
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
/* Defining the Capabilities */
@@ -24,11 +25,8 @@
MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\
MMC_CAP_SDIO_IRQ)
-static struct dw_mci_board pci_board_data = {
- .caps = DW_MCI_CAPABILITIES,
- .bus_hz = 33 * 1000 * 1000,
- .detect_delay_ms = 200,
- .fifo_depth = 32,
+static const struct dw_mci_drv_data pci_drv_data = {
+ .common_caps = DW_MCI_CAPABILITIES,
};
static int dw_mci_pci_probe(struct pci_dev *pdev,
@@ -41,20 +39,20 @@ static int dw_mci_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
- host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
+ host = dw_mci_alloc_host(&pdev->dev);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
host->irq = pdev->irq;
host->irq_flags = IRQF_SHARED;
- host->dev = &pdev->dev;
- host->pdata = &pci_board_data;
+ host->fifo_depth = 32;
+ host->detect_delay_ms = 200;
+ host->bus_hz = 33 * 1000 * 1000;
+ host->drv_data = &pci_drv_data;
- ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev));
- if (ret)
- return ret;
-
- host->regs = pcim_iomap_table(pdev)[PCI_BAR_NO];
+ host->regs = pcim_iomap_region(pdev, BAR_2, pci_name(pdev));
+ if (IS_ERR(host->regs))
+ return PTR_ERR(host->regs);
pci_set_master(pdev);
@@ -74,11 +72,6 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
dw_mci_remove(host);
}
-static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = {
- SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
- RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
-};
-
static const struct pci_device_id dw_mci_pci_id[] = {
{ PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) },
{}
@@ -91,7 +84,7 @@ static struct pci_driver dw_mci_pci_driver = {
.probe = dw_mci_pci_probe,
.remove = dw_mci_pci_remove,
.driver = {
- .pm = pm_ptr(&dw_mci_pci_dev_pm_ops),
+ .pm = pm_ptr(&dw_mci_pmops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index de820ffd2133..cf38bb118dc2 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -33,18 +33,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
struct dw_mci *host;
struct resource *regs;
- host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
+ host = dw_mci_alloc_host(&pdev->dev);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0)
return host->irq;
host->drv_data = drv_data;
- host->dev = &pdev->dev;
host->irq_flags = 0;
- host->pdata = pdev->dev.platform_data;
host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
if (IS_ERR(host->regs))
@@ -58,24 +56,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
-const struct dev_pm_ops dw_mci_pltfm_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
- dw_mci_runtime_resume,
- NULL)
-};
-EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
-
static int dw_mci_socfpga_priv_init(struct dw_mci *host)
{
struct device_node *np = host->dev->of_node;
+ struct mmc_clk_phase phase;
struct regmap *sys_mgr_base_addr;
- u32 clk_phase[2] = {0}, reg_offset, reg_shift;
- int i, rc, hs_timing;
+ u32 reg_offset, reg_shift;
+ int hs_timing;
- rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0);
- if (rc < 0)
+ phase = host->phase_map.phase[MMC_TIMING_SD_HS];
+ if (!phase.valid)
return 0;
sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
@@ -87,10 +77,10 @@ static int dw_mci_socfpga_priv_init(struct dw_mci *host)
of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
- for (i = 0; i < ARRAY_SIZE(clk_phase); i++)
- clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
+ phase.in_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
+ phase.out_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
- hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift);
+ hs_timing = SYSMGR_SDMMC_CTRL_SET(phase.in_deg, phase.out_deg, reg_shift);
regmap_write(sys_mgr_base_addr, reg_offset, hs_timing);
return 0;
@@ -136,7 +126,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
.name = "dw_mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_pltfm_match,
- .pm = &dw_mci_pltfm_pmops,
+ .pm = pm_ptr(&dw_mci_pmops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h
index 64cf7522a3d4..ef1b05d484c3 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.h
+++ b/drivers/mmc/host/dw_mmc-pltfm.h
@@ -11,6 +11,6 @@
extern int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data);
extern void dw_mci_pltfm_remove(struct platform_device *pdev);
-extern const struct dev_pm_ops dw_mci_pltfm_pmops;
+extern const struct dev_pm_ops dw_mci_pmops;
#endif /* _DW_MMC_PLTFM_H_ */
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index ac069d0c42b2..c6eece4ec3fd 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -179,7 +179,8 @@ static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees)
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;
+ struct mmc_clk_phase phase = host->phase_map.phase[ios->timing];
+ int ret, sample_phase, drv_phase;
unsigned int cclkin;
u32 bus_hz;
@@ -213,8 +214,15 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
}
/* Make sure we use phases which we can enumerate with */
- if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS)
- rockchip_mmc_set_phase(host, true, priv->default_sample_phase);
+ if (!IS_ERR(priv->sample_clk)) {
+ /* Keep backward compatibility */
+ if (ios->timing <= MMC_TIMING_SD_HS) {
+ sample_phase = phase.valid ? phase.in_deg : priv->default_sample_phase;
+ rockchip_mmc_set_phase(host, true, sample_phase);
+ } else if (phase.valid) {
+ rockchip_mmc_set_phase(host, true, phase.in_deg);
+ }
+ }
/*
* Set the drive phase offset based on speed mode to achieve hold times.
@@ -243,15 +251,13 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
* same results, for instance).
*/
if (!IS_ERR(priv->drv_clk)) {
- int phase;
-
/*
* In almost all cases a 90 degree phase offset will provide
* sufficient hold times across all valid input clock rates
* assuming delay_o is not absurd for a given SoC. We'll use
* that as a default.
*/
- phase = 90;
+ drv_phase = 90;
switch (ios->timing) {
case MMC_TIMING_MMC_DDR52:
@@ -261,7 +267,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
* to get the same timings.
*/
if (ios->bus_width == MMC_BUS_WIDTH_8)
- phase = 180;
+ drv_phase = 180;
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
@@ -273,22 +279,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
* SoCs measured this seems to be OK, but it doesn't
* hurt to give margin here, so we use 180.
*/
- phase = 180;
+ drv_phase = 180;
break;
}
- rockchip_mmc_set_phase(host, false, phase);
+ /* Use out phase from phase map first */
+ if (phase.valid)
+ drv_phase = phase.out_deg;
+ rockchip_mmc_set_phase(host, false, drv_phase);
}
}
#define TUNING_ITERATION_TO_PHASE(i, num_phases) \
(DIV_ROUND_UP((i) * 360, num_phases))
-static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+static int dw_mci_rk3288_execute_tuning(struct dw_mci *host, u32 opcode)
{
- struct dw_mci *host = slot->host;
struct dw_mci_rockchip_priv_data *priv = host->priv;
- struct mmc_host *mmc = slot->mmc;
+ struct mmc_host *mmc = host->mmc;
int ret = 0;
int i;
bool v, prev_v = 0, first_v;
@@ -476,8 +484,8 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
struct dw_mci_rockchip_priv_data *priv = host->priv;
int ret, i;
- /* It is slot 8 on Rockchip SoCs */
- host->sdio_id0 = 8;
+ /* SDIO irq is the 8th on Rockchip SoCs */
+ host->sdio_irq = 8;
if (of_device_is_compatible(host->dev->of_node, "rockchip,rk3288-dw-mshc")) {
host->bus_hz /= RK3288_CLKGEN_DIV;
diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c
index 34964b0dab21..11472a641cbe 100644
--- a/drivers/mmc/host/dw_mmc-starfive.c
+++ b/drivers/mmc/host/dw_mmc-starfive.c
@@ -53,11 +53,10 @@ static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase
mdelay(1);
}
-static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
+static int dw_mci_starfive_execute_tuning(struct dw_mci *host,
u32 opcode)
{
static const int grade = MAX_DELAY_CHAIN;
- struct dw_mci *host = slot->host;
int smpl_phase, smpl_raise = -1, smpl_fall = -1;
int ret;
@@ -65,7 +64,7 @@ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
dw_mci_starfive_set_sample_phase(host, smpl_phase);
mci_writel(host, RINTSTS, ALL_INT_CLR);
- ret = mmc_send_tuning(slot->mmc, opcode, NULL);
+ ret = mmc_send_tuning(host->mmc, opcode, NULL);
if (!ret && smpl_raise < 0) {
smpl_raise = smpl_phase;
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 07eb9f23b9d6..20193ee7b73e 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -7,35 +7,28 @@
* Copyright (C) 2009, 2010 Imagination Technologies Ltd.
*/
-#include <linux/blkdev.h>
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
-#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
-#include <linux/ioport.h>
-#include <linux/ktime.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/prandom.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/delay.h>
#include <linux/irq.h>
+#include <linux/ktime.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
-#include <linux/bitops.h>
-#include <linux/regulator/consumer.h>
-#include <linux/of.h>
#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include "dw_mmc.h"
@@ -47,8 +40,6 @@
SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \
DW_MCI_CMD_ERROR_FLAGS)
-#define DW_MCI_SEND_STATUS 1
-#define DW_MCI_RECV_STATUS 2
#define DW_MCI_DMA_THRESHOLD 16
#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */
@@ -107,15 +98,15 @@ struct idmac_desc {
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
{
- struct dw_mci_slot *slot = s->private;
+ struct dw_mci *host = s->private;
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_command *stop;
struct mmc_data *data;
/* Make sure we get a consistent snapshot */
- spin_lock_bh(&slot->host->lock);
- mrq = slot->mrq;
+ spin_lock_bh(&host->lock);
+ mrq = host->mrq;
if (mrq) {
cmd = mrq->cmd;
@@ -140,7 +131,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
stop->resp[2], stop->error);
}
- spin_unlock_bh(&slot->host->lock);
+ spin_unlock_bh(&host->lock);
return 0;
}
@@ -165,10 +156,9 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
}
DEFINE_SHOW_ATTRIBUTE(dw_mci_regs);
-static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+static void dw_mci_init_debugfs(struct dw_mci *host)
{
- struct mmc_host *mmc = slot->mmc;
- struct dw_mci *host = slot->host;
+ struct mmc_host *mmc = host->mmc;
struct dentry *root;
root = mmc->debugfs_root;
@@ -176,7 +166,7 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
return;
debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops);
- debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops);
+ debugfs_create_file("req", 0400, root, host, &dw_mci_req_fops);
debugfs_create_u32("state", 0400, root, &host->state);
debugfs_create_xul("pending_events", 0400, root,
&host->pending_events);
@@ -231,9 +221,8 @@ static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags)
}
}
-static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+static void mci_send_cmd(struct dw_mci *host, u32 cmd, u32 arg)
{
- struct dw_mci *host = slot->host;
unsigned int cmd_status = 0;
mci_writel(host, CMDARG, arg);
@@ -244,15 +233,14 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
if (readl_poll_timeout_atomic(host->regs + SDMMC_CMD, cmd_status,
!(cmd_status & SDMMC_CMD_START),
1, 500 * USEC_PER_MSEC))
- dev_err(&slot->mmc->class_dev,
+ dev_err(&host->mmc->class_dev,
"Timeout sending command (cmd %#x arg %#x status %#x)\n",
cmd, arg, cmd_status);
}
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
u32 cmdr;
cmd->error = -EINPROGRESS;
@@ -274,8 +262,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
cmdr |= SDMMC_CMD_VOLT_SWITCH;
/* Change state to continue to handle CMD11 weirdness */
- WARN_ON(slot->host->state != STATE_SENDING_CMD);
- slot->host->state = STATE_SENDING_CMD11;
+ WARN_ON(host->state != STATE_SENDING_CMD);
+ host->state = STATE_SENDING_CMD11;
/*
* We need to disable low power mode (automatic clock stop)
@@ -289,9 +277,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
* until the voltage change is all done.
*/
clk_en_a = mci_readl(host, CLKENA);
- clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id);
+ clk_en_a &= ~SDMMC_CLKEN_LOW_PWR;
mci_writel(host, CLKENA, clk_en_a);
- mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
+ mci_send_cmd(host, SDMMC_CMD_UPD_CLK |
SDMMC_CMD_PRV_DAT_WAIT, 0);
}
@@ -311,7 +299,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
cmdr |= SDMMC_CMD_DAT_WR;
}
- if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &slot->flags))
+ if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags))
cmdr |= SDMMC_CMD_USE_HOLD_REG;
return cmdr;
@@ -350,7 +338,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
cmdr = stop->opcode | SDMMC_CMD_STOP |
SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP;
- if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags))
+ if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags))
cmdr |= SDMMC_CMD_USE_HOLD_REG;
return cmdr;
@@ -480,7 +468,7 @@ static void dw_mci_dmac_complete_dma(void *arg)
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->slot->mmc),
+ dma_sync_sg_for_cpu(mmc_dev(host->mmc),
data->sg,
data->sg_len,
DMA_FROM_DEVICE);
@@ -575,23 +563,26 @@ static int dw_mci_idmac_init(struct dw_mci *host)
return 0;
}
-static inline int dw_mci_prepare_desc64(struct dw_mci *host,
- struct mmc_data *data,
- unsigned int sg_len)
+static inline int dw_mci_prepare_desc(struct dw_mci *host, struct mmc_data *data,
+ unsigned int sg_len, bool is_64bit)
{
unsigned int desc_len;
- struct idmac_desc_64addr *desc_first, *desc_last, *desc;
- u32 val;
- int i;
+ struct idmac_desc *desc_first, *desc_last, *desc;
+ struct idmac_desc_64addr *desc64_first, *desc64_last, *desc64;
+ u32 val, des0;
+ int i, err;
- desc_first = desc_last = desc = host->sg_cpu;
+ if (is_64bit)
+ desc64_first = desc64_last = desc64 = host->sg_cpu;
+ else
+ desc_first = desc_last = desc = host->sg_cpu;
for (i = 0; i < sg_len; i++) {
unsigned int length = sg_dma_len(&data->sg[i]);
u64 mem_addr = sg_dma_address(&data->sg[i]);
- for ( ; length ; desc++) {
+ while (length > 0) {
desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ?
length : DW_MCI_DESC_DATA_LENGTH;
@@ -603,113 +594,60 @@ static inline int dw_mci_prepare_desc64(struct dw_mci *host,
* isn't still owned by IDMAC as IDMAC's write
* ops and CPU's read ops are asynchronous.
*/
- if (readl_poll_timeout_atomic(&desc->des0, val,
- !(val & IDMAC_DES0_OWN),
- 10, 100 * USEC_PER_MSEC))
+ if (is_64bit)
+ err = readl_poll_timeout_atomic(&desc64->des0, val,
+ IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC);
+ else
+ err = readl_poll_timeout_atomic(&desc->des0, val,
+ IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC);
+ if (err)
goto err_own_bit;
- /*
- * Set the OWN bit and disable interrupts
- * for this descriptor
- */
- desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
- IDMAC_DES0_CH;
-
- /* Buffer length */
- IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len);
-
- /* Physical address to DMA to/from */
- desc->des4 = mem_addr & 0xffffffff;
- desc->des5 = mem_addr >> 32;
-
- /* Update physical address for the next desc */
- mem_addr += desc_len;
-
- /* Save pointer to the last descriptor */
- desc_last = desc;
- }
- }
-
- /* Set first descriptor */
- desc_first->des0 |= IDMAC_DES0_FD;
-
- /* Set last descriptor */
- desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
- desc_last->des0 |= IDMAC_DES0_LD;
-
- return 0;
-err_own_bit:
- /* restore the descriptor chain as it's polluted */
- dev_dbg(host->dev, "descriptor is still owned by IDMAC.\n");
- memset(host->sg_cpu, 0, DESC_RING_BUF_SZ);
- dw_mci_idmac_init(host);
- return -EINVAL;
-}
-
-
-static inline int dw_mci_prepare_desc32(struct dw_mci *host,
- struct mmc_data *data,
- unsigned int sg_len)
-{
- unsigned int desc_len;
- struct idmac_desc *desc_first, *desc_last, *desc;
- u32 val;
- int i;
-
- desc_first = desc_last = desc = host->sg_cpu;
-
- for (i = 0; i < sg_len; i++) {
- unsigned int length = sg_dma_len(&data->sg[i]);
-
- u32 mem_addr = sg_dma_address(&data->sg[i]);
-
- for ( ; length ; desc++) {
- desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ?
- length : DW_MCI_DESC_DATA_LENGTH;
-
- length -= desc_len;
-
- /*
- * Wait for the former clear OWN bit operation
- * of IDMAC to make sure that this descriptor
- * isn't still owned by IDMAC as IDMAC's write
- * ops and CPU's read ops are asynchronous.
- */
- if (readl_poll_timeout_atomic(&desc->des0, val,
- IDMAC_OWN_CLR64(val),
- 10,
- 100 * USEC_PER_MSEC))
- goto err_own_bit;
+ des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+ if (is_64bit)
+ desc64->des0 = des0;
+ else
+ desc->des0 = cpu_to_le32(des0);
/*
- * Set the OWN bit and disable interrupts
- * for this descriptor
+ * 1. Set OWN bit and disable interrupts for this descriptor
+ * 2. Set Buffer length
+ * Set physical address to DMA to/from
*/
- desc->des0 = cpu_to_le32(IDMAC_DES0_OWN |
- IDMAC_DES0_DIC |
- IDMAC_DES0_CH);
-
- /* Buffer length */
- IDMAC_SET_BUFFER1_SIZE(desc, desc_len);
-
- /* Physical address to DMA to/from */
- desc->des2 = cpu_to_le32(mem_addr);
+ if (is_64bit) {
+ desc64->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+ IDMAC_64ADDR_SET_BUFFER1_SIZE(desc64, desc_len);
+ desc64->des4 = mem_addr & 0xffffffff;
+ desc64->des5 = mem_addr >> 32;
+ } else {
+ IDMAC_SET_BUFFER1_SIZE(desc, desc_len);
+ desc->des2 = cpu_to_le32(mem_addr);
+ }
/* Update physical address for the next desc */
mem_addr += desc_len;
/* Save pointer to the last descriptor */
- desc_last = desc;
+ if (is_64bit) {
+ desc64_last = desc64;
+ desc64++;
+ } else {
+ desc_last = desc;
+ desc++;
+ }
}
}
- /* Set first descriptor */
- desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD);
-
- /* Set last descriptor */
- desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH |
- IDMAC_DES0_DIC));
- desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD);
+ /* Set the first descriptor and the last descriptor */
+ if (is_64bit) {
+ desc64_first->des0 |= IDMAC_DES0_FD;
+ desc64_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+ desc64_last->des0 |= IDMAC_DES0_LD;
+ } else {
+ desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD);
+ desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC));
+ desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD);
+ }
return 0;
err_own_bit:
@@ -725,11 +663,7 @@ static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
u32 temp;
int ret;
- if (host->dma_64bit_address == 1)
- ret = dw_mci_prepare_desc64(host, host->data, sg_len);
- else
- ret = dw_mci_prepare_desc32(host, host->data, sg_len);
-
+ ret = dw_mci_prepare_desc(host, host->data, sg_len, host->dma_64bit_address);
if (ret)
goto out;
@@ -823,7 +757,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host,
/* Flush cache before write */
if (host->data->flags & MMC_DATA_WRITE)
- dma_sync_sg_for_device(mmc_dev(host->slot->mmc), sgl,
+ dma_sync_sg_for_device(mmc_dev(host->mmc), sgl,
sg_elems, DMA_TO_DEVICE);
dma_async_issue_pending(host->dms->ch);
@@ -913,16 +847,16 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
static void dw_mci_pre_req(struct mmc_host *mmc,
struct mmc_request *mrq)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct dw_mci *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- if (!slot->host->use_dma || !data)
+ if (!host->use_dma || !data)
return;
/* This data might be unmapped at this time */
data->host_cookie = COOKIE_UNMAPPED;
- if (dw_mci_pre_dma_transfer(slot->host, mrq->data,
+ if (dw_mci_pre_dma_transfer(host, mrq->data,
COOKIE_PRE_MAPPED) < 0)
data->host_cookie = COOKIE_UNMAPPED;
}
@@ -931,14 +865,14 @@ static void dw_mci_post_req(struct mmc_host *mmc,
struct mmc_request *mrq,
int err)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct dw_mci *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- if (!slot->host->use_dma || !data)
+ if (!host->use_dma || !data)
return;
if (data->host_cookie != COOKIE_UNMAPPED)
- dma_unmap_sg(slot->host->dev,
+ dma_unmap_sg(host->dev,
data->sg,
data->sg_len,
mmc_get_dma_dir(data));
@@ -947,43 +881,21 @@ static void dw_mci_post_req(struct mmc_host *mmc,
static int dw_mci_get_cd(struct mmc_host *mmc)
{
- int present;
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
int gpio_cd = mmc_gpio_get_cd(mmc);
- /* Use platform get_cd function, else try onboard card detect */
- if (((mmc->caps & MMC_CAP_NEEDS_POLL)
- || !mmc_card_is_removable(mmc))) {
- present = 1;
-
- if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
- if (mmc->caps & MMC_CAP_NEEDS_POLL) {
- dev_info(&mmc->class_dev,
- "card is polling.\n");
- } else {
- dev_info(&mmc->class_dev,
- "card is non-removable.\n");
- }
- set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
- }
+ if (mmc->caps & MMC_CAP_NEEDS_POLL)
+ return 1;
- return present;
- } else if (gpio_cd >= 0)
- present = gpio_cd;
- else
- present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
- == 0 ? 1 : 0;
+ if (!mmc_card_is_removable(mmc))
+ return 1;
- spin_lock_bh(&host->lock);
- if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags))
- dev_dbg(&mmc->class_dev, "card is present\n");
- else if (!present &&
- !test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags))
- dev_dbg(&mmc->class_dev, "card is not present\n");
- spin_unlock_bh(&host->lock);
+ /* Try slot gpio detection */
+ if (gpio_cd >= 0)
+ return !!gpio_cd;
- return present;
+ /* Host native card detect */
+ return !(mci_readl(host, CDETECT) & BIT(0));
}
static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
@@ -1150,9 +1062,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
host->data = data;
if (data->flags & MMC_DATA_READ)
- host->dir_status = DW_MCI_RECV_STATUS;
+ host->dir_status = MMC_DATA_READ;
else
- host->dir_status = DW_MCI_SEND_STATUS;
+ host->dir_status = MMC_DATA_WRITE;
dw_mci_ctrl_thld(host, data);
@@ -1200,10 +1112,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
}
}
-static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
+static void dw_mci_setup_bus(struct dw_mci *host, bool force_clkinit)
{
- struct dw_mci *host = slot->host;
- unsigned int clock = slot->clock;
+ unsigned int clock = host->clock;
u32 div;
u32 clk_en_a;
u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT;
@@ -1212,11 +1123,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
if (host->state == STATE_WAITING_CMD11_DONE)
sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
- slot->mmc->actual_clock = 0;
+ host->mmc->actual_clock = 0;
if (!clock) {
mci_writel(host, CLKENA, 0);
- mci_send_cmd(slot, sdmmc_cmd_bits, 0);
+ mci_send_cmd(host, sdmmc_cmd_bits, 0);
} else if (clock != host->current_speed || force_clkinit) {
div = host->bus_hz / clock;
if (host->bus_hz % clock && host->bus_hz > clock)
@@ -1228,14 +1139,14 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
- if ((clock != slot->__clk_old &&
- !test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) ||
+ if ((clock != host->clk_old &&
+ !test_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags)) ||
force_clkinit) {
/* Silent the verbose log if calling from PM context */
if (!force_clkinit)
- dev_info(&slot->mmc->class_dev,
- "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
- slot->id, host->bus_hz, clock,
+ dev_info(&host->mmc->class_dev,
+ "Bus speed = %dHz (req %dHz, actual %dHZ div = %d)\n",
+ host->bus_hz, clock,
div ? ((host->bus_hz / div) >> 1) :
host->bus_hz, div);
@@ -1243,9 +1154,9 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
* If card is polling, display the message only
* one time at boot time.
*/
- if (slot->mmc->caps & MMC_CAP_NEEDS_POLL &&
- slot->mmc->f_min == clock)
- set_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags);
+ if (host->mmc->caps & MMC_CAP_NEEDS_POLL &&
+ host->mmc->f_min == clock)
+ set_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags);
}
/* disable clock */
@@ -1253,33 +1164,33 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKSRC, 0);
/* inform CIU */
- mci_send_cmd(slot, sdmmc_cmd_bits, 0);
+ mci_send_cmd(host, sdmmc_cmd_bits, 0);
/* set clock to desired speed */
mci_writel(host, CLKDIV, div);
/* inform CIU */
- mci_send_cmd(slot, sdmmc_cmd_bits, 0);
+ mci_send_cmd(host, sdmmc_cmd_bits, 0);
/* enable clock; only low power if no SDIO */
- clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
- if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags))
- clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
+ clk_en_a = SDMMC_CLKEN_ENABLE;
+ if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags))
+ clk_en_a |= SDMMC_CLKEN_LOW_PWR;
mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */
- mci_send_cmd(slot, sdmmc_cmd_bits, 0);
+ mci_send_cmd(host, sdmmc_cmd_bits, 0);
/* keep the last clock value that was requested from core */
- slot->__clk_old = clock;
- slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) :
+ host->clk_old = clock;
+ host->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) :
host->bus_hz;
}
host->current_speed = clock;
- /* Set the current slot bus width */
- mci_writel(host, CTYPE, (slot->ctype << slot->id));
+ /* Set the current bus width */
+ mci_writel(host, CTYPE, host->ctype);
}
static void dw_mci_set_data_timeout(struct dw_mci *host,
@@ -1313,15 +1224,13 @@ static void dw_mci_set_data_timeout(struct dw_mci *host,
timeout_ns, tmout >> 8);
}
-static void __dw_mci_start_request(struct dw_mci *host,
- struct dw_mci_slot *slot,
- struct mmc_command *cmd)
+static void dw_mci_start_request(struct dw_mci *host, struct mmc_command *cmd)
{
struct mmc_request *mrq;
struct mmc_data *data;
u32 cmdflags;
- mrq = slot->mrq;
+ mrq = host->mrq;
host->mrq = mrq;
@@ -1338,10 +1247,10 @@ static void __dw_mci_start_request(struct dw_mci *host,
mci_writel(host, BLKSIZ, data->blksz);
}
- cmdflags = dw_mci_prepare_command(slot->mmc, cmd);
+ cmdflags = dw_mci_prepare_command(host->mmc, cmd);
/* this is the first command, send the initialization clock */
- if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
+ if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &host->flags))
cmdflags |= SDMMC_CMD_INIT;
if (data) {
@@ -1374,27 +1283,32 @@ static void __dw_mci_start_request(struct dw_mci *host,
host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
}
-static void dw_mci_start_request(struct dw_mci *host,
- struct dw_mci_slot *slot)
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
- struct mmc_request *mrq = slot->mrq;
+ struct dw_mci *host = mmc_priv(mmc);
struct mmc_command *cmd;
- cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
- __dw_mci_start_request(host, slot, cmd);
-}
+ WARN_ON(host->mrq);
-/* must be called with host->lock held */
-static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
- struct mmc_request *mrq)
-{
- dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
- host->state);
+ /*
+ * The check for card presence and queueing of the request must be
+ * atomic, otherwise the card could be removed in between and the
+ * request wouldn't fail until another card was inserted.
+ */
+ if (!dw_mci_get_cd(mmc)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
- slot->mrq = mrq;
+ spin_lock_bh(&host->lock);
+
+ dev_vdbg(&host->mmc->class_dev, "request: state=%d\n",
+ host->state);
+ host->mrq = mrq;
if (host->state == STATE_WAITING_CMD11_DONE) {
- dev_warn(&slot->mmc->class_dev,
+ dev_warn(&host->mmc->class_dev,
"Voltage change didn't complete\n");
/*
* this case isn't expected to happen, so we can
@@ -1406,168 +1320,119 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
if (host->state == STATE_IDLE) {
host->state = STATE_SENDING_CMD;
- dw_mci_start_request(host, slot);
- } else {
- list_add_tail(&slot->queue_node, &host->queue);
- }
-}
-
-static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
-{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
-
- WARN_ON(slot->mrq);
-
- /*
- * The check for card presence and queueing of the request must be
- * atomic, otherwise the card could be removed in between and the
- * request wouldn't fail until another card was inserted.
- */
-
- if (!dw_mci_get_cd(mmc)) {
- mrq->cmd->error = -ENOMEDIUM;
- mmc_request_done(mmc, mrq);
- return;
+ cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
+ dw_mci_start_request(host, cmd);
}
- spin_lock_bh(&host->lock);
-
- dw_mci_queue_request(host, slot, mrq);
-
spin_unlock_bh(&host->lock);
}
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
+ struct dw_mci *host = mmc_priv(mmc);
+ const struct dw_mci_drv_data *drv_data = host->drv_data;
u32 regs;
int ret;
switch (ios->bus_width) {
case MMC_BUS_WIDTH_4:
- slot->ctype = SDMMC_CTYPE_4BIT;
+ host->ctype = SDMMC_CTYPE_4BIT;
break;
case MMC_BUS_WIDTH_8:
- slot->ctype = SDMMC_CTYPE_8BIT;
+ host->ctype = SDMMC_CTYPE_8BIT;
break;
default:
/* set default 1 bit mode */
- slot->ctype = SDMMC_CTYPE_1BIT;
+ host->ctype = SDMMC_CTYPE_1BIT;
}
- regs = mci_readl(slot->host, UHS_REG);
+ regs = mci_readl(host, UHS_REG);
/* 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);
+ regs |= BIT(16);
else
- regs &= ~((0x1 << slot->id) << 16);
+ regs &= ~BIT(16);
- mci_writel(slot->host, UHS_REG, regs);
- slot->host->timing = ios->timing;
+ mci_writel(host, UHS_REG, regs);
+ host->timing = ios->timing;
/*
* Use mirror of ios->clock to prevent race with mmc
* core ios update when finding the minimum.
*/
- slot->clock = ios->clock;
+ host->clock = ios->clock;
if (drv_data && drv_data->set_ios)
- drv_data->set_ios(slot->host, ios);
+ drv_data->set_ios(host, ios);
switch (ios->power_mode) {
case MMC_POWER_UP:
- if (!IS_ERR(mmc->supply.vmmc)) {
- ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
- ios->vdd);
- if (ret) {
- dev_err(slot->host->dev,
- "failed to enable vmmc regulator\n");
- /*return, if failed turn on vmmc*/
- return;
- }
+ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
+ if (ret) {
+ dev_err(host->dev, "failed to enable vmmc regulator\n");
+ return;
}
- set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
- regs = mci_readl(slot->host, PWREN);
- regs |= (1 << slot->id);
- mci_writel(slot->host, PWREN, regs);
+ set_bit(DW_MMC_CARD_NEED_INIT, &host->flags);
+ regs = mci_readl(host, PWREN);
+ regs |= BIT(0);
+ mci_writel(host, PWREN, regs);
break;
case MMC_POWER_ON:
- if (!slot->host->vqmmc_enabled) {
- if (!IS_ERR(mmc->supply.vqmmc)) {
- ret = regulator_enable(mmc->supply.vqmmc);
- if (ret < 0)
- dev_err(slot->host->dev,
- "failed to enable vqmmc\n");
- else
- slot->host->vqmmc_enabled = true;
-
- } else {
- /* Keep track so we don't reset again */
- slot->host->vqmmc_enabled = true;
- }
-
- /* Reset our state machine after powering on */
- dw_mci_ctrl_reset(slot->host,
- SDMMC_CTRL_ALL_RESET_FLAGS);
- }
-
+ mmc_regulator_enable_vqmmc(mmc);
/* Adjust clock / bus width after power is up */
- dw_mci_setup_bus(slot, false);
+ dw_mci_setup_bus(host, false);
break;
case MMC_POWER_OFF:
/* Turn clock off before power goes down */
- dw_mci_setup_bus(slot, false);
+ dw_mci_setup_bus(host, false);
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
- if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled)
- regulator_disable(mmc->supply.vqmmc);
- slot->host->vqmmc_enabled = false;
+ mmc_regulator_disable_vqmmc(mmc);
- regs = mci_readl(slot->host, PWREN);
- regs &= ~(1 << slot->id);
- mci_writel(slot->host, PWREN, regs);
+ regs = mci_readl(host, PWREN);
+ regs &= ~BIT(0);
+ mci_writel(host, PWREN, regs);
+ /* Reset our state machine after powering off */
+ dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS);
break;
default:
break;
}
- if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
- slot->host->state = STATE_IDLE;
+ if (host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
+ host->state = STATE_IDLE;
}
static int dw_mci_card_busy(struct mmc_host *mmc)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct dw_mci *host = mmc_priv(mmc);
u32 status;
/*
* Check the busy bit which is low when DAT[3:0]
* (the data lines) are 0000
*/
- status = mci_readl(slot->host, STATUS);
+ status = mci_readl(host, STATUS);
return !!(status & SDMMC_STATUS_BUSY);
}
static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
const struct dw_mci_drv_data *drv_data = host->drv_data;
u32 uhs;
- u32 v18 = SDMMC_UHS_18V << slot->id;
+ u32 v18 = SDMMC_UHS_18V;
int ret;
if (drv_data && drv_data->switch_voltage)
- return drv_data->switch_voltage(mmc, ios);
+ return drv_data->switch_voltage(host, ios);
/*
* Program the voltage. Note that some instances of dw_mmc may use
@@ -1597,7 +1462,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
static int dw_mci_get_ro(struct mmc_host *mmc)
{
int read_only;
- struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct dw_mci *host = mmc_priv(mmc);
int gpio_ro = mmc_gpio_get_ro(mmc);
/* Use platform get_ro function, else try on board write protect */
@@ -1605,7 +1470,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
read_only = gpio_ro;
else
read_only =
- mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+ mci_readl(host, WRTPRT) & BIT(0) ? 1 : 0;
dev_dbg(&mmc->class_dev, "card is %s\n",
read_only ? "read-only" : "read-write");
@@ -1615,8 +1480,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
static void dw_mci_hw_reset(struct mmc_host *mmc)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
const struct dw_mci_drv_data *drv_data = host->drv_data;
int reset;
@@ -1639,18 +1503,17 @@ static void dw_mci_hw_reset(struct mmc_host *mmc)
* tRSTH >= 1us: RST_n high period
*/
reset = mci_readl(host, RST_N);
- reset &= ~(SDMMC_RST_HWACTIVE << slot->id);
+ reset &= ~SDMMC_RST_HWACTIVE;
mci_writel(host, RST_N, reset);
usleep_range(1, 2);
- reset |= SDMMC_RST_HWACTIVE << slot->id;
+ reset |= SDMMC_RST_HWACTIVE;
mci_writel(host, RST_N, reset);
usleep_range(200, 300);
}
-static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare)
+static void dw_mci_prepare_sdio_irq(struct dw_mci *host, bool prepare)
{
- struct dw_mci *host = slot->host;
- const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
+ const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR;
u32 clk_en_a_old;
u32 clk_en_a;
@@ -1662,23 +1525,22 @@ static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare)
clk_en_a_old = mci_readl(host, CLKENA);
if (prepare) {
- set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
+ set_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags);
clk_en_a = clk_en_a_old & ~clken_low_pwr;
} else {
- clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
+ clear_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags);
clk_en_a = clk_en_a_old | clken_low_pwr;
}
if (clk_en_a != clk_en_a_old) {
mci_writel(host, CLKENA, clk_en_a);
- mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT,
+ mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT,
0);
}
}
-static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb)
+static void __dw_mci_enable_sdio_irq(struct dw_mci *host, int enb)
{
- struct dw_mci *host = slot->host;
unsigned long irqflags;
u32 int_mask;
@@ -1687,9 +1549,9 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb)
/* Enable/disable Slot Specific SDIO interrupt */
int_mask = mci_readl(host, INTMASK);
if (enb)
- int_mask |= SDMMC_INT_SDIO(slot->sdio_id);
+ int_mask |= SDMMC_INT_SDIO(host->sdio_irq);
else
- int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id);
+ int_mask &= ~SDMMC_INT_SDIO(host->sdio_irq);
mci_writel(host, INTMASK, int_mask);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
@@ -1697,11 +1559,10 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb)
static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
- dw_mci_prepare_sdio_irq(slot, enb);
- __dw_mci_enable_sdio_irq(slot, enb);
+ dw_mci_prepare_sdio_irq(host, enb);
+ __dw_mci_enable_sdio_irq(host, enb);
/* Avoid runtime suspending the device when SDIO IRQ is enabled */
if (enb)
@@ -1712,28 +1573,26 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
static void dw_mci_ack_sdio_irq(struct mmc_host *mmc)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct dw_mci *host = mmc_priv(mmc);
- __dw_mci_enable_sdio_irq(slot, 1);
+ __dw_mci_enable_sdio_irq(host, 1);
}
static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
const struct dw_mci_drv_data *drv_data = host->drv_data;
int err = -EINVAL;
if (drv_data && drv_data->execute_tuning)
- err = drv_data->execute_tuning(slot, opcode);
+ err = drv_data->execute_tuning(host, opcode);
return err;
}
static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc,
struct mmc_ios *ios)
{
- struct dw_mci_slot *slot = mmc_priv(mmc);
- struct dw_mci *host = slot->host;
+ struct dw_mci *host = mmc_priv(mmc);
const struct dw_mci_drv_data *drv_data = host->drv_data;
if (drv_data && drv_data->prepare_hs400_tuning)
@@ -1804,7 +1663,7 @@ static bool dw_mci_reset(struct dw_mci *host)
ciu_out:
/* After a CTRL reset we need to have CIU set clock registers */
- mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0);
+ mci_send_cmd(host, SDMMC_CMD_UPD_CLK, 0);
return ret;
}
@@ -1895,29 +1754,16 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
__releases(&host->lock)
__acquires(&host->lock)
{
- struct dw_mci_slot *slot;
- struct mmc_host *prev_mmc = host->slot->mmc;
+ struct mmc_host *prev_mmc = host->mmc;
WARN_ON(host->cmd || host->data);
- host->slot->mrq = NULL;
host->mrq = NULL;
- if (!list_empty(&host->queue)) {
- slot = list_entry(host->queue.next,
- struct dw_mci_slot, queue_node);
- list_del(&slot->queue_node);
- dev_vdbg(host->dev, "list not empty: %s is next\n",
- mmc_hostname(slot->mmc));
- host->state = STATE_SENDING_CMD;
- dw_mci_start_request(host, slot);
- } else {
- dev_vdbg(host->dev, "list empty\n");
- if (host->state == STATE_SENDING_CMD11)
- host->state = STATE_WAITING_CMD11_DONE;
- else
- host->state = STATE_IDLE;
- }
+ if (host->state == STATE_SENDING_CMD11)
+ host->state = STATE_WAITING_CMD11_DONE;
+ else
+ host->state = STATE_IDLE;
spin_unlock(&host->lock);
mmc_request_done(prev_mmc, mrq);
@@ -1968,7 +1814,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
data->error = -EILSEQ;
} else if (status & SDMMC_INT_EBE) {
if (host->dir_status ==
- DW_MCI_SEND_STATUS) {
+ MMC_DATA_WRITE) {
/*
* No data CRC status was returned.
* The number of bytes transferred
@@ -1977,7 +1823,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else if (host->dir_status ==
- DW_MCI_RECV_STATUS) {
+ MMC_DATA_READ) {
data->error = -EILSEQ;
}
} else {
@@ -2095,8 +1941,7 @@ static void dw_mci_work_func(struct work_struct *t)
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
- __dw_mci_start_request(host, host->slot,
- mrq->cmd);
+ dw_mci_start_request(host, mrq->cmd);
goto unlock;
}
@@ -2123,7 +1968,7 @@ static void dw_mci_work_func(struct work_struct *t)
* avoids races and keeps things simple.
*/
if (err != -ETIMEDOUT &&
- host->dir_status == DW_MCI_RECV_STATUS) {
+ host->dir_status == MMC_DATA_READ) {
state = STATE_SENDING_DATA;
continue;
}
@@ -2167,7 +2012,7 @@ static void dw_mci_work_func(struct work_struct *t)
* If all data-related interrupts don't come
* within the given time in reading data state.
*/
- if (host->dir_status == DW_MCI_RECV_STATUS)
+ if (host->dir_status == MMC_DATA_READ)
dw_mci_set_drto(host);
break;
}
@@ -2207,7 +2052,7 @@ static void dw_mci_work_func(struct work_struct *t)
* interrupt doesn't come within the given time.
* in reading data state.
*/
- if (host->dir_status == DW_MCI_RECV_STATUS)
+ if (host->dir_status == MMC_DATA_READ)
dw_mci_set_drto(host);
break;
}
@@ -2803,17 +2648,14 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
static void dw_mci_handle_cd(struct dw_mci *host)
{
- struct dw_mci_slot *slot = host->slot;
-
- mmc_detect_change(slot->mmc,
- msecs_to_jiffies(host->pdata->detect_delay_ms));
+ mmc_detect_change(host->mmc,
+ msecs_to_jiffies(host->detect_delay_ms));
}
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
struct dw_mci *host = dev_id;
u32 pending;
- struct dw_mci_slot *slot = host->slot;
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
@@ -2878,7 +2720,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (!host->data_status)
host->data_status = pending;
smp_wmb(); /* drain writebuffer */
- if (host->dir_status == DW_MCI_RECV_STATUS) {
+ if (host->dir_status == MMC_DATA_READ) {
if (host->sg != NULL)
dw_mci_read_data_pio(host, true);
}
@@ -2890,13 +2732,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_RXDR) {
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
- if (host->dir_status == DW_MCI_RECV_STATUS && host->sg)
+ if (host->dir_status == MMC_DATA_READ && host->sg)
dw_mci_read_data_pio(host, false);
}
if (pending & SDMMC_INT_TXDR) {
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
- if (host->dir_status == DW_MCI_SEND_STATUS && host->sg)
+ if (host->dir_status == MMC_DATA_WRITE && host->sg)
dw_mci_write_data_pio(host);
}
@@ -2914,11 +2756,11 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
dw_mci_handle_cd(host);
}
- if (pending & SDMMC_INT_SDIO(slot->sdio_id)) {
+ if (pending & SDMMC_INT_SDIO(host->sdio_irq)) {
mci_writel(host, RINTSTS,
- SDMMC_INT_SDIO(slot->sdio_id));
- __dw_mci_enable_sdio_irq(slot, 0);
- sdio_signal_irq(slot->mmc);
+ SDMMC_INT_SDIO(host->sdio_irq));
+ __dw_mci_enable_sdio_irq(host, 0);
+ sdio_signal_irq(host->mmc);
}
}
@@ -2950,29 +2792,19 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
+static int dw_mci_init_host_caps(struct dw_mci *host)
{
- struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = host->drv_data;
- struct mmc_host *mmc = slot->mmc;
+ struct mmc_host *mmc = host->mmc;
int ctrl_id;
- if (host->pdata->caps)
- mmc->caps = host->pdata->caps;
-
- if (host->pdata->pm_caps)
- mmc->pm_caps = host->pdata->pm_caps;
-
if (drv_data)
mmc->caps |= drv_data->common_caps;
- if (host->dev->of_node) {
- ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
- if (ctrl_id < 0)
- ctrl_id = 0;
- } else {
+ if (host->dev->of_node)
+ ctrl_id = mmc->index;
+ else
ctrl_id = to_platform_device(host->dev)->id;
- }
if (drv_data && drv_data->caps) {
if (ctrl_id >= drv_data->num_caps) {
@@ -2983,9 +2815,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
mmc->caps |= drv_data->caps[ctrl_id];
}
- if (host->pdata->caps2)
- mmc->caps2 = host->pdata->caps2;
-
/* if host has set a minimum_freq, we should respect it */
if (host->minimum_speed)
mmc->f_min = host->minimum_speed;
@@ -3002,23 +2831,11 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
return 0;
}
-static int dw_mci_init_slot(struct dw_mci *host)
+static int dw_mci_init_host(struct dw_mci *host)
{
- struct mmc_host *mmc;
- struct dw_mci_slot *slot;
+ struct mmc_host *mmc = host->mmc;
int ret;
- mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot));
- if (!mmc)
- return -ENOMEM;
-
- slot = mmc_priv(mmc);
- slot->id = 0;
- slot->sdio_id = host->sdio_id0 + slot->id;
- slot->mmc = mmc;
- slot->host = host;
- host->slot = slot;
-
mmc->ops = &dw_mci_ops;
/*if there are external regulators, get them*/
@@ -3033,7 +2850,9 @@ static int dw_mci_init_slot(struct dw_mci *host)
if (ret)
return ret;
- ret = dw_mci_init_slot_caps(slot);
+ mmc_of_parse_clk_phase(host->dev, &host->phase_map);
+
+ ret = dw_mci_init_host_caps(host);
if (ret)
return ret;
@@ -3061,6 +2880,11 @@ static int dw_mci_init_slot(struct dw_mci *host)
mmc->max_seg_size = mmc->max_req_size;
}
+ if (mmc->caps & MMC_CAP_NEEDS_POLL)
+ dev_info(&mmc->class_dev, "card is polling.\n");
+ else if (!mmc_card_is_removable(mmc))
+ dev_info(&mmc->class_dev, "card is non-removable.\n");
+
dw_mci_get_cd(mmc);
ret = mmc_add_host(mmc);
@@ -3068,17 +2892,16 @@ static int dw_mci_init_slot(struct dw_mci *host)
return ret;
#if defined(CONFIG_DEBUG_FS)
- dw_mci_init_debugfs(slot);
+ dw_mci_init_debugfs(host);
#endif
return 0;
}
-static void dw_mci_cleanup_slot(struct dw_mci_slot *slot)
+static void dw_mci_cleanup_host(struct dw_mci *host)
{
/* Debugfs stuff is cleaned up by mmc core */
- mmc_remove_host(slot->mmc);
- slot->host->slot = NULL;
+ mmc_remove_host(host->mmc);
}
static void dw_mci_init_dma(struct dw_mci *host)
@@ -3291,54 +3114,43 @@ exit:
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
-#ifdef CONFIG_OF
-static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
+static int dw_mci_parse_dt(struct dw_mci *host)
{
- struct dw_mci_board *pdata;
struct device *dev = host->dev;
const struct dw_mci_drv_data *drv_data = host->drv_data;
int ret;
u32 clock_frequency;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return ERR_PTR(-ENOMEM);
-
/* find reset controller when exist */
- pdata->rstc = devm_reset_control_get_optional_exclusive(dev, "reset");
- if (IS_ERR(pdata->rstc))
- return ERR_CAST(pdata->rstc);
+ host->rstc = devm_reset_control_get_optional_exclusive(dev, "reset");
+ if (IS_ERR(host->rstc))
+ return PTR_ERR(host->rstc);
- if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
+ if (!host->fifo_depth && device_property_read_u32(dev, "fifo-depth", &host->fifo_depth))
dev_info(dev,
"fifo-depth property not found, using value of FIFOTH register as default\n");
- device_property_read_u32(dev, "card-detect-delay",
- &pdata->detect_delay_ms);
+ if (!host->detect_delay_ms)
+ device_property_read_u32(dev, "card-detect-delay",
+ &host->detect_delay_ms);
- device_property_read_u32(dev, "data-addr", &host->data_addr_override);
+ if (!host->data_addr_override)
+ device_property_read_u32(dev, "data-addr", &host->data_addr_override);
if (device_property_present(dev, "fifo-watermark-aligned"))
host->wm_aligned = true;
- if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency))
- pdata->bus_hz = clock_frequency;
+ if (!host->bus_hz && !device_property_read_u32(dev, "clock-frequency", &clock_frequency))
+ host->bus_hz = clock_frequency;
if (drv_data && drv_data->parse_dt) {
ret = drv_data->parse_dt(host);
if (ret)
- return ERR_PTR(ret);
+ return ret;
}
- return pdata;
-}
-
-#else /* CONFIG_OF */
-static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
-{
- return ERR_PTR(-EINVAL);
+ return 0;
}
-#endif /* CONFIG_OF */
static void dw_mci_enable_cd(struct dw_mci *host)
{
@@ -3346,13 +3158,13 @@ static void dw_mci_enable_cd(struct dw_mci *host)
u32 temp;
/*
- * No need for CD if all slots have a non-error GPIO
+ * No need for CD if host has a non-error GPIO
* as well as broken card detection is found.
*/
- if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL)
+ if (host->mmc->caps & MMC_CAP_NEEDS_POLL)
return;
- if (mmc_gpio_get_cd(host->slot->mmc) < 0) {
+ if (mmc_gpio_get_cd(host->mmc) < 0) {
spin_lock_irqsave(&host->irq_lock, irqflags);
temp = mci_readl(host, INTMASK);
temp |= SDMMC_INT_CD;
@@ -3361,18 +3173,32 @@ static void dw_mci_enable_cd(struct dw_mci *host)
}
}
+struct dw_mci *dw_mci_alloc_host(struct device *dev)
+{
+ struct mmc_host *mmc;
+ struct dw_mci *host;
+
+ mmc = devm_mmc_alloc_host(dev, sizeof(struct dw_mci));
+ if (!mmc)
+ return ERR_PTR(-ENOMEM);
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->dev = dev;
+
+ return host;
+}
+EXPORT_SYMBOL(dw_mci_alloc_host);
+
int dw_mci_probe(struct dw_mci *host)
{
const struct dw_mci_drv_data *drv_data = host->drv_data;
int width, i, ret = 0;
u32 fifo_size;
- if (!host->pdata) {
- host->pdata = dw_mci_parse_dt(host);
- if (IS_ERR(host->pdata))
- return dev_err_probe(host->dev, PTR_ERR(host->pdata),
- "platform data not available\n");
- }
+ ret = dw_mci_parse_dt(host);
+ if (ret)
+ return dev_err_probe(host->dev, ret, "parse dt failed\n");
host->biu_clk = devm_clk_get(host->dev, "biu");
if (IS_ERR(host->biu_clk)) {
@@ -3395,8 +3221,6 @@ int dw_mci_probe(struct dw_mci *host)
ret = PTR_ERR(host->ciu_clk);
if (ret == -EPROBE_DEFER)
goto err_clk_biu;
-
- host->bus_hz = host->pdata->bus_hz;
} else {
ret = clk_prepare_enable(host->ciu_clk);
if (ret) {
@@ -3404,12 +3228,12 @@ int dw_mci_probe(struct dw_mci *host)
goto err_clk_biu;
}
- if (host->pdata->bus_hz) {
- ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
+ if (host->bus_hz) {
+ ret = clk_set_rate(host->ciu_clk, host->bus_hz);
if (ret)
dev_warn(host->dev,
"Unable to set bus rate to %uHz\n",
- host->pdata->bus_hz);
+ host->bus_hz);
}
host->bus_hz = clk_get_rate(host->ciu_clk);
}
@@ -3421,10 +3245,10 @@ int dw_mci_probe(struct dw_mci *host)
goto err_clk_ciu;
}
- if (host->pdata->rstc) {
- reset_control_assert(host->pdata->rstc);
+ if (host->rstc) {
+ reset_control_assert(host->rstc);
usleep_range(10, 50);
- reset_control_deassert(host->pdata->rstc);
+ reset_control_deassert(host->rstc);
}
if (drv_data && drv_data->init) {
@@ -3442,7 +3266,6 @@ int dw_mci_probe(struct dw_mci *host)
spin_lock_init(&host->lock);
spin_lock_init(&host->irq_lock);
- INIT_LIST_HEAD(&host->queue);
dw_mci_init_fault(host);
@@ -3483,7 +3306,6 @@ int dw_mci_probe(struct dw_mci *host)
goto err_clk_ciu;
}
- host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host);
/* Clear the interrupts for the host controller */
@@ -3497,7 +3319,7 @@ int dw_mci_probe(struct dw_mci *host)
* FIFO threshold settings RxMark = fifo_size / 2 - 1,
* Tx Mark = fifo_size / 2 DMA Size = 8
*/
- if (!host->pdata->fifo_depth) {
+ if (!host->fifo_depth) {
/*
* Power-on value of RX_WMark is FIFO_DEPTH-1, but this may
* have been overwritten by the bootloader, just like we're
@@ -3507,7 +3329,7 @@ int dw_mci_probe(struct dw_mci *host)
fifo_size = mci_readl(host, FIFOTH);
fifo_size = 1 + ((fifo_size >> 16) & 0xfff);
} else {
- fifo_size = host->pdata->fifo_depth;
+ fifo_size = host->fifo_depth;
}
host->fifo_depth = fifo_size;
host->fifoth_val =
@@ -3552,14 +3374,13 @@ int dw_mci_probe(struct dw_mci *host)
"DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n",
host->irq, width, fifo_size);
- /* We need at least one slot to succeed */
- ret = dw_mci_init_slot(host);
+ ret = dw_mci_init_host(host);
if (ret) {
- dev_dbg(host->dev, "slot %d init failed\n", i);
+ dev_dbg(host->dev, "host init failed\n");
goto err_dmaunmap;
}
- /* Now that slots are all setup, we can enable card detect */
+ /* Now that host is setup, we can enable card detect */
dw_mci_enable_cd(host);
return 0;
@@ -3568,7 +3389,7 @@ err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
- reset_control_assert(host->pdata->rstc);
+ reset_control_assert(host->rstc);
err_clk_ciu:
clk_disable_unprepare(host->ciu_clk);
@@ -3582,9 +3403,8 @@ EXPORT_SYMBOL(dw_mci_probe);
void dw_mci_remove(struct dw_mci *host)
{
- dev_dbg(host->dev, "remove slot\n");
- if (host->slot)
- dw_mci_cleanup_slot(host->slot);
+ dev_dbg(host->dev, "remove host\n");
+ dw_mci_cleanup_host(host);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
@@ -3596,16 +3416,13 @@ void dw_mci_remove(struct dw_mci *host)
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
- reset_control_assert(host->pdata->rstc);
+ reset_control_assert(host->rstc);
clk_disable_unprepare(host->ciu_clk);
clk_disable_unprepare(host->biu_clk);
}
EXPORT_SYMBOL(dw_mci_remove);
-
-
-#ifdef CONFIG_PM
int dw_mci_runtime_suspend(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
@@ -3615,9 +3432,8 @@ int dw_mci_runtime_suspend(struct device *dev)
clk_disable_unprepare(host->ciu_clk);
- if (host->slot &&
- (mmc_host_can_gpio_cd(host->slot->mmc) ||
- !mmc_card_is_removable(host->slot->mmc)))
+ if (mmc_host_can_gpio_cd(host->mmc) ||
+ !mmc_card_is_removable(host->mmc))
clk_disable_unprepare(host->biu_clk);
return 0;
@@ -3629,9 +3445,8 @@ int dw_mci_runtime_resume(struct device *dev)
int ret = 0;
struct dw_mci *host = dev_get_drvdata(dev);
- if (host->slot &&
- (mmc_host_can_gpio_cd(host->slot->mmc) ||
- !mmc_card_is_removable(host->slot->mmc))) {
+ if (mmc_host_can_gpio_cd(host->mmc) ||
+ !mmc_card_is_removable(host->mmc)) {
ret = clk_prepare_enable(host->biu_clk);
if (ret)
return ret;
@@ -3647,8 +3462,11 @@ int dw_mci_runtime_resume(struct device *dev)
goto err;
}
- if (host->use_dma && host->dma_ops->init)
- host->dma_ops->init(host);
+ if (host->use_dma && host->dma_ops->init) {
+ ret = host->dma_ops->init(host);
+ if (ret)
+ return ret;
+ }
/*
* Restore the initial value at FIFOTH register
@@ -3667,44 +3485,35 @@ int dw_mci_runtime_resume(struct device *dev)
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
- if (host->slot && host->slot->mmc->pm_flags & MMC_PM_KEEP_POWER)
- dw_mci_set_ios(host->slot->mmc, &host->slot->mmc->ios);
+ if (host->mmc->pm_flags & MMC_PM_KEEP_POWER)
+ dw_mci_set_ios(host->mmc, &host->mmc->ios);
/* Force setup bus to guarantee available clock output */
- dw_mci_setup_bus(host->slot, true);
+ dw_mci_setup_bus(host, true);
/* Re-enable SDIO interrupts. */
- if (sdio_irq_claimed(host->slot->mmc))
- __dw_mci_enable_sdio_irq(host->slot, 1);
+ if (sdio_irq_claimed(host->mmc))
+ __dw_mci_enable_sdio_irq(host, 1);
- /* Now that slots are all setup, we can enable card detect */
+ /* Now that host is setup, we can enable card detect */
dw_mci_enable_cd(host);
return 0;
err:
- if (host->slot &&
- (mmc_host_can_gpio_cd(host->slot->mmc) ||
- !mmc_card_is_removable(host->slot->mmc)))
+ if (mmc_host_can_gpio_cd(host->mmc) ||
+ !mmc_card_is_removable(host->mmc))
clk_disable_unprepare(host->biu_clk);
return ret;
}
EXPORT_SYMBOL(dw_mci_runtime_resume);
-#endif /* CONFIG_PM */
-
-static int __init dw_mci_init(void)
-{
- pr_info("Synopsys Designware Multimedia Card Interface Driver\n");
- return 0;
-}
-
-static void __exit dw_mci_exit(void)
-{
-}
-module_init(dw_mci_init);
-module_exit(dw_mci_exit);
+const struct dev_pm_ops dw_mci_pmops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(dw_mci_pmops);
MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
MODULE_AUTHOR("NXP Semiconductor VietNam");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 648b4a5641bf..42e58be74ce0 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -43,8 +43,6 @@ enum dw_mci_cookie {
COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */
};
-struct mmc_data;
-
enum {
TRANS_MODE_PIO = 0,
TRANS_MODE_IDMAC,
@@ -57,14 +55,14 @@ struct dw_mci_dma_slave {
};
/**
- * struct dw_mci - MMC controller state shared between all slots
+ * struct dw_mci - MMC controller state
* @lock: Spinlock protecting the queue and associated data.
* @irq_lock: Spinlock protecting the INTMASK setting.
* @regs: Pointer to MMIO registers.
* @fifo_reg: Pointer to MMIO registers for data FIFO
* @sg: Scatterlist entry currently being processed by PIO code, if any.
* @sg_miter: PIO mapping scatterlist iterator.
- * @mrq: The request currently being processed on @slot,
+ * @mrq: The request currently being processed on @host,
* or NULL if the controller is idle.
* @cmd: The command currently being sent to the card, or NULL.
* @data: The data currently being transferred, or NULL if no data
@@ -78,7 +76,7 @@ struct dw_mci_dma_slave {
* @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
* @sg_dma: Bus address of DMA buffer.
* @sg_cpu: Virtual address of DMA buffer.
- * @dma_ops: Pointer to platform-specific DMA callbacks.
+ * @dma_ops: Pointer to DMA callbacks.
* @cmd_status: Snapshot of SR taken upon completion of the current
* @ring_size: Buffer size for idma descriptors.
* command. Only valid when EVENT_CMD_COMPLETE is pending.
@@ -96,7 +94,6 @@ struct dw_mci_dma_slave {
* @completed_events: Bitmask of events which the state machine has
* processed.
* @state: BH work state.
- * @queue: List of slots waiting for access to the controller.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
* @current_speed: Configured rate of the controller.
@@ -104,12 +101,10 @@ struct dw_mci_dma_slave {
* @fifoth_val: The value of FIFOTH register.
* @verid: Denote Version ID.
* @dev: Device associated with the MMC controller.
- * @pdata: Platform data associated with the MMC controller.
* @drv_data: Driver specific data for identified variant of the controller
* @priv: Implementation defined private data.
* @biu_clk: Pointer to bus interface unit clock instance.
* @ciu_clk: Pointer to card interface unit clock instance.
- * @slot: Slots sharing this MMC controller.
* @fifo_depth: depth of FIFO.
* @data_addr_override: override fifo reg offset with this value.
* @wm_aligned: force fifo watermark equal with data length in PIO mode.
@@ -121,23 +116,28 @@ struct dw_mci_dma_slave {
* @push_data: Pointer to FIFO push function.
* @pull_data: Pointer to FIFO pull function.
* @quirks: Set of quirks that apply to specific versions of the IP.
- * @vqmmc_enabled: Status of vqmmc, should be true or false.
* @irq_flags: The flags to be passed to request_irq.
* @irq: The irq value to be passed to request_irq.
- * @sdio_id0: Number of slot0 in the SDIO interrupt registers.
+ * @sdio_irq: SDIO interrupt bit in interrupt registers.
* @cmd11_timer: Timer for SD3.0 voltage switch over scheme.
* @cto_timer: Timer for broken command transfer over scheme.
* @dto_timer: Timer for broken data transfer over scheme.
+ * @mmc: The mmc_host representing this dw_mci.
+ * @flags: Random state bits associated with the host.
+ * @ctype: Card type for this host.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @clk_old: The last clock value that was requested from core.
+ * @pdev: platform_device registered
+ * @rstc: Reset controller for this host.
+ * @detect_delay_ms: Delay in mS before detecting cards after interrupt.
+ * @phase_map: The map for recording in and out phases for each timing
*
* Locking
* =======
*
- * @lock is a softirq-safe spinlock protecting @queue as well as
- * @slot, @mrq and @state. These must always be updated
+ * @lock is a softirq-safe spinlock protecting as well as
+ * @mrq and @state. These must always be updated
* at the same time while holding @lock.
- * The @mrq field of struct dw_mci_slot is also protected by @lock,
- * and must always be written at the same time as the slot is added to
- * @queue.
*
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
* to allow the interrupt handler to modify it directly. Held for only long
@@ -199,7 +199,6 @@ struct dw_mci {
unsigned long pending_events;
unsigned long completed_events;
enum dw_mci_state state;
- struct list_head queue;
u32 bus_hz;
u32 current_speed;
@@ -207,7 +206,6 @@ struct dw_mci {
u32 fifoth_val;
u16 verid;
struct device *dev;
- struct dw_mci_board *pdata;
const struct dw_mci_drv_data *drv_data;
void *priv;
struct clk *biu_clk;
@@ -228,11 +226,10 @@ struct dw_mci {
void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
u32 quirks;
- bool vqmmc_enabled;
unsigned long irq_flags; /* IRQ flags */
int irq;
- int sdio_id0;
+ int sdio_irq;
struct timer_list cmd11_timer;
struct timer_list cto_timer;
@@ -242,6 +239,19 @@ struct dw_mci {
struct fault_attr fail_data_crc;
struct hrtimer fault_timer;
#endif
+ struct mmc_host *mmc;
+ unsigned long flags;
+#define DW_MMC_CARD_NEED_INIT 0
+#define DW_MMC_CARD_NO_LOW_PWR 1
+#define DW_MMC_CARD_NO_USE_HOLD 2
+#define DW_MMC_CARD_NEEDS_POLL 3
+ u32 ctype;
+ unsigned int clock;
+ unsigned int clk_old;
+ struct platform_device *pdev;
+ struct reset_control *rstc;
+ u32 detect_delay_ms;
+ struct mmc_clk_phase_map phase_map;
};
/* DMA ops for Internal/External DMAC interface */
@@ -255,30 +265,6 @@ struct dw_mci_dma_ops {
void (*exit)(struct dw_mci *host);
};
-struct dma_pdata;
-
-/* Board platform data */
-struct dw_mci_board {
- unsigned int bus_hz; /* Clock speed at the cclk_in pad */
-
- u32 caps; /* Capabilities */
- u32 caps2; /* More capabilities */
- u32 pm_caps; /* PM capabilities */
- /*
- * Override fifo depth. If 0, autodetect it from the FIFOTH register,
- * but note that this may not be reliable after a bootloader has used
- * it.
- */
- unsigned int fifo_depth;
-
- /* delay in mS before detecting cards after interrupt */
- u32 detect_delay_ms;
-
- struct reset_control *rstc;
- struct dw_mci_dma_ops *dma_ops;
- struct dma_pdata *data;
-};
-
/* Support for longer data read timeout */
#define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0)
/* Force 32-bit access to the FIFO */
@@ -396,7 +382,6 @@ struct dw_mci_board {
#define SDMMC_INT_CMD_DONE BIT(2)
#define SDMMC_INT_RESP_ERR BIT(1)
#define SDMMC_INT_CD BIT(0)
-#define SDMMC_INT_ERROR 0xbfc2
/* Command register defines */
#define SDMMC_CMD_START BIT(31)
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
@@ -505,84 +490,17 @@ static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
#define mci_writel(dev, reg, value) \
writel_relaxed((value), (dev)->regs + SDMMC_##reg)
-/* 16-bit FIFO access macros */
-#define mci_readw(dev, reg) \
- readw_relaxed((dev)->regs + SDMMC_##reg)
-#define mci_writew(dev, reg, value) \
- writew_relaxed((value), (dev)->regs + SDMMC_##reg)
-
-/* 64-bit FIFO access macros */
-#ifdef readq
-#define mci_readq(dev, reg) \
- readq_relaxed((dev)->regs + SDMMC_##reg)
-#define mci_writeq(dev, reg, value) \
- writeq_relaxed((value), (dev)->regs + SDMMC_##reg)
-#else
-/*
- * Dummy readq implementation for architectures that don't define it.
- *
- * We would assume that none of these architectures would configure
- * the IP block with a 64bit FIFO width, so this code will never be
- * executed on those machines. Defining these macros here keeps the
- * rest of the code free from ifdefs.
- */
-#define mci_readq(dev, reg) \
- (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg))
-#define mci_writeq(dev, reg, value) \
- (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
-
+#ifndef readq
#define __raw_writeq(__value, __reg) \
(*(volatile u64 __force *)(__reg) = (__value))
#define __raw_readq(__reg) (*(volatile u64 __force *)(__reg))
#endif
+extern struct dw_mci *dw_mci_alloc_host(struct device *device);
extern int dw_mci_probe(struct dw_mci *host);
extern void dw_mci_remove(struct dw_mci *host);
-#ifdef CONFIG_PM
extern int dw_mci_runtime_suspend(struct device *device);
extern int dw_mci_runtime_resume(struct device *device);
-#else
-static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOTSUPP; }
-static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; }
-#endif
-
-/**
- * struct dw_mci_slot - MMC slot state
- * @mmc: The mmc_host representing this slot.
- * @host: The MMC controller this slot is using.
- * @ctype: Card type for this slot.
- * @mrq: mmc_request currently being processed or waiting to be
- * processed, or NULL when the slot is idle.
- * @queue_node: List node for placing this node in the @queue list of
- * &struct dw_mci.
- * @clock: Clock rate configured by set_ios(). Protected by host->lock.
- * @__clk_old: The last clock value that was requested from core.
- * Keeping track of this helps us to avoid spamming the console.
- * @flags: Random state bits associated with the slot.
- * @id: Number of this slot.
- * @sdio_id: Number of this slot in the SDIO interrupt registers.
- */
-struct dw_mci_slot {
- struct mmc_host *mmc;
- struct dw_mci *host;
-
- u32 ctype;
-
- struct mmc_request *mrq;
- struct list_head queue_node;
-
- unsigned int clock;
- unsigned int __clk_old;
-
- unsigned long flags;
-#define DW_MMC_CARD_PRESENT 0
-#define DW_MMC_CARD_NEED_INIT 1
-#define DW_MMC_CARD_NO_LOW_PWR 2
-#define DW_MMC_CARD_NO_USE_HOLD 3
-#define DW_MMC_CARD_NEEDS_POLL 4
- int id;
- int sdio_id;
-};
/**
* dw_mci driver data - dw-mshc implementation specific driver data.
@@ -609,10 +527,10 @@ struct dw_mci_drv_data {
int (*init)(struct dw_mci *host);
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, u32 opcode);
+ int (*execute_tuning)(struct dw_mci *host, u32 opcode);
int (*prepare_hs400_tuning)(struct dw_mci *host,
struct mmc_ios *ios);
- int (*switch_voltage)(struct mmc_host *mmc,
+ int (*switch_voltage)(struct dw_mci *host,
struct mmc_ios *ios);
void (*set_data_timeout)(struct dw_mci *host,
unsigned int timeout_ns);
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 6a0d0250d47b..6a3c26b7c82d 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -1052,7 +1052,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
host = mmc_priv(mmc);
/* Default if no match is JZ4740 */
- host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev);
+ host->version = (unsigned long)device_get_match_data(&pdev->dev);
ret = mmc_of_parse(mmc);
if (ret)
diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c
index da3daab5f3d6..f553e92fd9e5 100644
--- a/drivers/mmc/host/loongson2-mmc.c
+++ b/drivers/mmc/host/loongson2-mmc.c
@@ -189,6 +189,12 @@
#define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000
#define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500
+/*
+ * Due to a hardware design flaw, the Loongson-2K0300 may fail to recognize the
+ * CMD48 (SD_READ_EXTR_SINGLE) interrupt.
+ */
+#define LOONGSON2_MMC_CMD48_QUIRK BIT(0)
+
/* Loongson-2K1000 SDIO2 DMA routing register */
#define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15)
#define LS2K1000_DMA0_CONF 0x0
@@ -245,6 +251,7 @@ struct loongson2_mmc_host {
};
struct loongson2_mmc_pdata {
+ u32 flags;
const struct regmap_config *regmap_config;
void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
@@ -568,6 +575,12 @@ static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct loongson2_mmc_host *host = mmc_priv(mmc);
+ if ((host->pdata->flags & LOONGSON2_MMC_CMD48_QUIRK) &&
+ mrq->cmd->opcode == SD_READ_EXTR_SINGLE) {
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
host->cmd_is_stop = 0;
host->mrq = mrq;
loongson2_mmc_send_request(mmc);
@@ -703,14 +716,6 @@ static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host,
return 0;
}
-static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
- .regmap_config = &ls2k0500_mmc_regmap_config,
- .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
- .setting_dma = ls2k0500_mmc_set_external_dma,
- .prepare_dma = loongson2_mmc_prepare_external_dma,
- .release_dma = loongson2_mmc_release_external_dma,
-};
-
static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
struct platform_device *pdev)
{
@@ -735,14 +740,6 @@ static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
return 0;
}
-static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
- .regmap_config = &ls2k0500_mmc_regmap_config,
- .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
- .setting_dma = ls2k1000_mmc_set_external_dma,
- .prepare_dma = loongson2_mmc_prepare_external_dma,
- .release_dma = loongson2_mmc_release_external_dma,
-};
-
static const struct regmap_config ls2k2000_mmc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
@@ -846,7 +843,6 @@ static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host,
if (!host->sg_cpu)
return -ENOMEM;
- memset(host->sg_cpu, 0, PAGE_SIZE);
return 0;
}
@@ -856,7 +852,36 @@ static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host,
dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
}
+static struct loongson2_mmc_pdata ls2k0300_mmc_pdata = {
+ .flags = LOONGSON2_MMC_CMD48_QUIRK,
+ .regmap_config = &ls2k2000_mmc_regmap_config,
+ .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data,
+ .fix_data_timeout = ls2k2000_mmc_fix_data_timeout,
+ .setting_dma = ls2k2000_mmc_set_internal_dma,
+ .prepare_dma = loongson2_mmc_prepare_internal_dma,
+ .release_dma = loongson2_mmc_release_internal_dma,
+};
+
+static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
+ .flags = 0,
+ .regmap_config = &ls2k0500_mmc_regmap_config,
+ .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
+ .setting_dma = ls2k0500_mmc_set_external_dma,
+ .prepare_dma = loongson2_mmc_prepare_external_dma,
+ .release_dma = loongson2_mmc_release_external_dma,
+};
+
+static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
+ .flags = 0,
+ .regmap_config = &ls2k0500_mmc_regmap_config,
+ .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
+ .setting_dma = ls2k1000_mmc_set_external_dma,
+ .prepare_dma = loongson2_mmc_prepare_external_dma,
+ .release_dma = loongson2_mmc_release_external_dma,
+};
+
static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = {
+ .flags = 0,
.regmap_config = &ls2k2000_mmc_regmap_config,
.reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data,
.fix_data_timeout = ls2k2000_mmc_fix_data_timeout,
@@ -985,6 +1010,7 @@ static void loongson2_mmc_remove(struct platform_device *pdev)
}
static const struct of_device_id loongson2_mmc_of_ids[] = {
+ { .compatible = "loongson,ls2k0300-mmc", .data = &ls2k0300_mmc_pdata },
{ .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
{ .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
{ .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata },
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index daed659f63f6..b2680cc054bd 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -203,9 +203,10 @@
#define SDC_CFG_DTOC GENMASK(31, 24) /* RW */
/* SDC_STS mask */
-#define SDC_STS_SDCBUSY BIT(0) /* RW */
-#define SDC_STS_CMDBUSY BIT(1) /* RW */
-#define SDC_STS_SWR_COMPL BIT(31) /* RW */
+#define SDC_STS_SDCBUSY BIT(0) /* RW */
+#define SDC_STS_CMDBUSY BIT(1) /* RW */
+#define SDC_STS_SPM_RESOURCE_RELEASE BIT(3) /* RW */
+#define SDC_STS_SWR_COMPL BIT(31) /* RW */
/* SDC_ADV_CFG0 mask */
#define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */
@@ -448,6 +449,7 @@ struct mtk_mmc_compatible {
bool use_internal_cd;
bool support_new_tx;
bool support_new_rx;
+ bool support_spm_res_release;
};
struct msdc_tune_para {
@@ -673,6 +675,25 @@ static const struct mtk_mmc_compatible mt8516_compat = {
.stop_dly_sel = 3,
};
+static const struct mtk_mmc_compatible mt8189_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = false,
+ .hs400_tune = false,
+ .needs_top_base = true,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = false,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .stop_dly_sel = 3,
+ .pop_en_cnt = 8,
+ .enhance_rx = true,
+ .support_64g = true,
+ .support_new_tx = false,
+ .support_new_rx = false,
+ .support_spm_res_release = true,
+};
+
static const struct mtk_mmc_compatible mt8196_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = false,
@@ -703,6 +724,7 @@ static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
+ { .compatible = "mediatek,mt8189-mmc", .data = &mt8189_compat},
{ .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat},
{ .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
@@ -3296,6 +3318,10 @@ static int msdc_runtime_suspend(struct device *dev)
__msdc_enable_sdio_irq(host, 0);
}
+
+ if (host->dev_comp->support_spm_res_release)
+ sdr_set_bits(host->base + SDC_STS, SDC_STS_SPM_RESOURCE_RELEASE);
+
msdc_gate_clock(host);
return 0;
}
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 2a310a145785..f9ec78d699f4 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -26,6 +26,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
+#include <linux/mux/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl-state.h>
#include <linux/platform_data/tmio.h>
@@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
struct regulator_dev *rdev;
struct renesas_sdhi_dma *dma_priv;
struct device *dev = &pdev->dev;
+ struct mux_state *mux_state;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
"state_uhs");
}
+ mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
+ if (IS_ERR(mux_state))
+ return PTR_ERR(mux_state);
+
host = tmio_mmc_host_alloc(pdev, mmc_data);
if (IS_ERR(host))
return PTR_ERR(host);
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 543ad1d0ed1c..9215600f03a2 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -456,19 +456,15 @@ static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev)
of_device_get_match_data(&pdev->dev), NULL);
}
-static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
- tmio_mmc_host_runtime_resume,
- NULL)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(renesas_sdhi_sys_dmac_dev_pm_ops,
+ tmio_mmc_host_runtime_suspend,
+ tmio_mmc_host_runtime_resume, NULL);
static struct platform_driver renesas_sys_dmac_sdhi_driver = {
.driver = {
.name = "sh_mobile_sdhi",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &renesas_sdhi_sys_dmac_dev_pm_ops,
+ .pm = pm_ptr(&renesas_sdhi_sys_dmac_dev_pm_ops),
.of_match_table = renesas_sdhi_sys_dmac_of_match,
},
.probe = renesas_sdhi_sys_dmac_probe,
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 4db3328f46df..8dfbc62f165b 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1181,79 +1181,6 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
return cd;
}
-static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err;
- u8 stat;
-
- /* Reference to Signal Voltage Switch Sequence in SD spec.
- * Wait for a period of time so that the card can drive SD_CMD and
- * SD_DAT[3:0] to low after sending back CMD11 response.
- */
- mdelay(1);
-
- /* SD_CMD, SD_DAT[3:0] should be driven to low by card;
- * If either one of SD_CMD,SD_DAT[3:0] is not low,
- * abort the voltage switch sequence;
- */
- err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
- if (err < 0)
- return err;
-
- if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
- SD_DAT1_STATUS | SD_DAT0_STATUS))
- return -EINVAL;
-
- /* Stop toggle SD clock */
- err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
- 0xFF, SD_CLK_FORCE_STOP);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err;
- u8 stat, mask, val;
-
- /* Wait 1.8V output of voltage regulator in card stable */
- msleep(50);
-
- /* Toggle SD clock again */
- err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN);
- if (err < 0)
- return err;
-
- /* Wait for a period of time so that the card can drive
- * SD_DAT[3:0] to high at 1.8V
- */
- msleep(20);
-
- /* SD_CMD, SD_DAT[3:0] should be pulled high by host */
- err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
- if (err < 0)
- return err;
-
- mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
- SD_DAT1_STATUS | SD_DAT0_STATUS;
- val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
- SD_DAT1_STATUS | SD_DAT0_STATUS;
- if ((stat & mask) != val) {
- dev_dbg(sdmmc_dev(host),
- "%s: SD_BUS_STAT = 0x%x\n", __func__, stat);
- rtsx_pci_write_register(pcr, SD_BUS_STAT,
- SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
- rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0);
- return -EINVAL;
- }
-
- return 0;
-}
-
static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
@@ -1281,7 +1208,9 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
voltage = OUTPUT_1V8;
if (voltage == OUTPUT_1V8) {
- err = sd_wait_voltage_stable_1(host);
+ /* Stop toggle SD clock */
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ 0xFF, SD_CLK_FORCE_STOP);
if (err < 0)
goto out;
}
@@ -1290,16 +1219,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
if (err < 0)
goto out;
- if (voltage == OUTPUT_1V8) {
- err = sd_wait_voltage_stable_2(host);
- if (err < 0)
- goto out;
- }
-
out:
/* Stop toggle SD clock in idle */
- err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
- SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+ if (err < 0)
+ rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
mutex_unlock(&pcr->pcr_mutex);
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index a7a5df673b0f..18ecddd6df6f 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -216,6 +216,8 @@
#define ESDHC_FLAG_DUMMY_PAD BIT(19)
#define ESDHC_AUTO_TUNING_WINDOW 3
+/* 100ms timeout for data inhibit */
+#define ESDHC_DATA_INHIBIT_WAIT_US 100000
enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
@@ -321,6 +323,14 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
.quirks = SDHCI_QUIRK_NO_LED,
};
+static struct esdhc_soc_data usdhc_s32n79_data = {
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
+ | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
+ | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
+ | ESDHC_FLAG_SKIP_ERR004536,
+ .quirks = SDHCI_QUIRK_NO_LED,
+};
+
static struct esdhc_soc_data usdhc_imx7ulp_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
@@ -408,6 +418,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, },
{ .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, },
{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
+ { .compatible = "nxp,s32n79-usdhc", .data = &usdhc_s32n79_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
@@ -1453,6 +1464,22 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
static void esdhc_reset(struct sdhci_host *host, u8 mask)
{
+ u32 present_state;
+ int ret;
+
+ /*
+ * For data or full reset, ensure any active data transfer completes
+ * before resetting to avoid system hang.
+ */
+ if (mask & (SDHCI_RESET_DATA | SDHCI_RESET_ALL)) {
+ ret = readl_poll_timeout_atomic(host->ioaddr + ESDHC_PRSSTAT, present_state,
+ !(present_state & SDHCI_DATA_INHIBIT), 2,
+ ESDHC_DATA_INHIBIT_WAIT_US);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "timeout waiting for data transfer completion\n");
+ }
+
sdhci_and_cqhci_reset(host, mask);
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
@@ -1795,8 +1822,6 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
of_property_read_u32(np, "fsl,strobe-dll-delay-target",
&boarddata->strobe_dll_delay_target);
- if (of_property_read_bool(np, "no-1-8-v"))
- host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
boarddata->delay_line = 0;
@@ -1815,9 +1840,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
if (ret)
return ret;
- /* HS400/HS400ES require 8 bit bus */
- if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA))
- host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
+ sdhci_get_property(pdev);
if (mmc_gpio_get_cd(host->mmc) >= 0)
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 3b85233131b3..633462c0be5f 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -157,6 +157,17 @@
#define CQHCI_VENDOR_CFG1 0xA00
#define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13)
+/* non command queue crypto enable register*/
+#define NONCQ_CRYPTO_PARM 0x70
+#define NONCQ_CRYPTO_DUN 0x74
+
+#define DISABLE_CRYPTO BIT(15)
+#define CRYPTO_GENERAL_ENABLE BIT(1)
+#define HC_VENDOR_SPECIFIC_FUNC4 0x260
+
+#define ICE_HCI_PARAM_CCI GENMASK(7, 0)
+#define ICE_HCI_PARAM_CE GENMASK(8, 8)
+
struct sdhci_msm_offset {
u32 core_hc_mode;
u32 core_mci_data_cnt;
@@ -300,6 +311,7 @@ struct sdhci_msm_host {
u32 dll_config;
u32 ddr_config;
bool vqmmc_enabled;
+ bool non_cqe_ice_init_done;
};
static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
@@ -1914,11 +1926,6 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
if (IS_ERR_OR_NULL(ice))
return PTR_ERR_OR_ZERO(ice);
- if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) {
- dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n");
- return 0;
- }
-
msm_host->ice = ice;
/* Initialize the blk_crypto_profile */
@@ -1932,7 +1939,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
profile->ll_ops = sdhci_msm_crypto_ops;
profile->max_dun_bytes_supported = 4;
- profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
+ profile->key_types_supported = qcom_ice_get_supported_key_type(ice);
profile->dev = dev;
/*
@@ -2012,9 +2019,111 @@ static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile,
return qcom_ice_evict_key(msm_host->ice, slot);
}
+static int sdhci_msm_ice_derive_sw_secret(struct blk_crypto_profile *profile,
+ const u8 *eph_key, size_t eph_key_size,
+ u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
+{
+ struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_derive_sw_secret(msm_host->ice, eph_key, eph_key_size,
+ sw_secret);
+}
+
+static int sdhci_msm_ice_import_key(struct blk_crypto_profile *profile,
+ const u8 *raw_key, size_t raw_key_size,
+ u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+ struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_import_key(msm_host->ice, raw_key, raw_key_size, lt_key);
+}
+
+static int sdhci_msm_ice_generate_key(struct blk_crypto_profile *profile,
+ u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+ struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_generate_key(msm_host->ice, lt_key);
+}
+
+static int sdhci_msm_ice_prepare_key(struct blk_crypto_profile *profile,
+ const u8 *lt_key, size_t lt_key_size,
+ u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+ struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_prepare_key(msm_host->ice, lt_key, lt_key_size, eph_key);
+}
+
+static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_host *mmc = msm_host->mmc;
+ struct cqhci_host *cq_host = mmc->cqe_private;
+ u32 config;
+
+ config = sdhci_readl(host, HC_VENDOR_SPECIFIC_FUNC4);
+ config &= ~DISABLE_CRYPTO;
+ sdhci_writel(host, config, HC_VENDOR_SPECIFIC_FUNC4);
+ config = cqhci_readl(cq_host, CQHCI_CFG);
+ config |= CRYPTO_GENERAL_ENABLE;
+ cqhci_writel(cq_host, config, CQHCI_CFG);
+}
+
+static void sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_host *mmc = msm_host->mmc;
+ struct cqhci_host *cq_host = mmc->cqe_private;
+ unsigned int crypto_params = 0;
+ int key_index;
+
+ if (mrq->crypto_ctx) {
+ if (!msm_host->non_cqe_ice_init_done) {
+ sdhci_msm_non_cqe_ice_init(host);
+ msm_host->non_cqe_ice_init_done = true;
+ }
+
+ key_index = mrq->crypto_key_slot;
+ crypto_params = FIELD_PREP(ICE_HCI_PARAM_CE, 1) |
+ FIELD_PREP(ICE_HCI_PARAM_CCI, key_index);
+
+ cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM);
+ cqhci_writel(cq_host, lower_32_bits(mrq->crypto_ctx->bc_dun[0]),
+ NONCQ_CRYPTO_DUN);
+ } else {
+ cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM);
+ }
+
+ /* Ensure crypto configuration is written before proceeding */
+ wmb();
+}
+
+/*
+ * Handle non-CQE MMC requests with ICE crypto support.
+ * Configures ICE registers before passing the request to
+ * the standard SDHCI handler.
+ */
+static void sdhci_msm_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ /* Only need to handle non-CQE crypto requests in this path */
+ if (mmc->caps2 & MMC_CAP2_CRYPTO)
+ sdhci_msm_ice_cfg(host, mrq);
+
+ sdhci_request(mmc, mrq);
+}
+
static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {
.keyslot_program = sdhci_msm_ice_keyslot_program,
.keyslot_evict = sdhci_msm_ice_keyslot_evict,
+ .derive_sw_secret = sdhci_msm_ice_derive_sw_secret,
+ .import_key = sdhci_msm_ice_import_key,
+ .generate_key = sdhci_msm_ice_generate_key,
+ .prepare_key = sdhci_msm_ice_prepare_key,
};
#else /* CONFIG_MMC_CRYPTO */
@@ -2762,6 +2871,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
+#ifdef CONFIG_MMC_CRYPTO
+ host->mmc_host_ops.request = sdhci_msm_request;
+#endif
/* Set the timeout value to max possible */
host->max_timeout_count = 0xF;
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index ab7f0ffe7b4f..785d3acb18c5 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -152,8 +152,7 @@ struct sdhci_arasan_clk_ops {
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
* @sampleclk_hw: Struct for the clock we might provide to a PHY.
* @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw.
- * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes
- * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes
+ * @phase_map: Struct for mmc_clk_phase_map provided.
* @set_clk_delays: Function pointer for setting Clock Delays
* @clk_of_data: Platform specific runtime clock data storage pointer
*/
@@ -162,8 +161,7 @@ struct sdhci_arasan_clk_data {
struct clk *sdcardclk;
struct clk_hw sampleclk_hw;
struct clk *sampleclk;
- int clk_phase_in[MMC_TIMING_MMC_HS400 + 1];
- int clk_phase_out[MMC_TIMING_MMC_HS400 + 1];
+ struct mmc_clk_phase_map phase_map;
void (*set_clk_delays)(struct sdhci_host *host);
void *clk_of_data;
};
@@ -1249,36 +1247,9 @@ static void sdhci_arasan_set_clk_delays(struct sdhci_host *host)
struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data;
clk_set_phase(clk_data->sampleclk,
- clk_data->clk_phase_in[host->timing]);
+ clk_data->phase_map.phase[host->timing].in_deg);
clk_set_phase(clk_data->sdcardclk,
- clk_data->clk_phase_out[host->timing]);
-}
-
-static void arasan_dt_read_clk_phase(struct device *dev,
- struct sdhci_arasan_clk_data *clk_data,
- unsigned int timing, const char *prop)
-{
- struct device_node *np = dev->of_node;
-
- u32 clk_phase[2] = {0};
- int ret;
-
- /*
- * Read Tap Delay values from DT, if the DT does not contain the
- * Tap Values then use the pre-defined values.
- */
- ret = of_property_read_variable_u32_array(np, prop, &clk_phase[0],
- 2, 0);
- if (ret < 0) {
- dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
- prop, clk_data->clk_phase_in[timing],
- clk_data->clk_phase_out[timing]);
- return;
- }
-
- /* The values read are Input and Output Clock Delays in order */
- clk_data->clk_phase_in[timing] = clk_phase[0];
- clk_data->clk_phase_out[timing] = clk_phase[1];
+ clk_data->phase_map.phase[host->timing].out_deg);
}
/**
@@ -1315,8 +1286,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
}
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
- clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i];
- clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i];
+ clk_data->phase_map.phase[i].in_deg = zynqmp_iclk_phase[i];
+ clk_data->phase_map.phase[i].out_deg = zynqmp_oclk_phase[i];
}
}
@@ -1327,8 +1298,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
VERSAL_OCLK_PHASE;
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
- clk_data->clk_phase_in[i] = versal_iclk_phase[i];
- clk_data->clk_phase_out[i] = versal_oclk_phase[i];
+ clk_data->phase_map.phase[i].in_deg = versal_iclk_phase[i];
+ clk_data->phase_map.phase[i].out_deg = versal_oclk_phase[i];
}
}
if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) {
@@ -1338,32 +1309,12 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
VERSAL_NET_EMMC_OCLK_PHASE;
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
- clk_data->clk_phase_in[i] = versal_net_iclk_phase[i];
- clk_data->clk_phase_out[i] = versal_net_oclk_phase[i];
+ clk_data->phase_map.phase[i].in_deg = versal_net_iclk_phase[i];
+ clk_data->phase_map.phase[i].out_deg = versal_net_oclk_phase[i];
}
}
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
- "clk-phase-legacy");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
- "clk-phase-mmc-hs");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
- "clk-phase-sd-hs");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
- "clk-phase-uhs-sdr12");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
- "clk-phase-uhs-sdr25");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
- "clk-phase-uhs-sdr50");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
- "clk-phase-uhs-sdr104");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
- "clk-phase-uhs-ddr50");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
- "clk-phase-mmc-ddr52");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
- "clk-phase-mmc-hs200");
- arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
- "clk-phase-mmc-hs400");
+
+ mmc_of_parse_clk_phase(dev, &clk_data->phase_map);
}
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
@@ -1512,6 +1463,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
.clk_ops = &arasan_clk_ops,
};
+static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = {
+ .ops = &sdhci_arasan_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_CQE,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = {
+ .pdata = &sdhci_arasan_axiado_pdata,
+ .clk_ops = &arasan_clk_ops,
+};
+
static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
{
@@ -1538,6 +1500,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "intel,keembay-sdhci-5.1-sdio",
.data = &intel_keembay_sdio_data,
},
+ {
+ .compatible = "axiado,ax3000-sdhci-5.1-emmc",
+ .data = &sdhci_arasan_axiado_data,
+ },
/* Generic compatible below here */
{
.compatible = "arasan,sdhci-8.9a",
diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c
index ca97b01996b1..f5d973783cbe 100644
--- a/drivers/mmc/host/sdhci-of-aspeed.c
+++ b/drivers/mmc/host/sdhci-of-aspeed.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/spinlock.h>
#include "sdhci-pltfm.h"
@@ -519,7 +520,8 @@ static struct platform_driver aspeed_sdhci_driver = {
static int aspeed_sdc_probe(struct platform_device *pdev)
{
- struct device_node *parent, *child;
+ struct reset_control *reset;
+ struct device_node *parent;
struct aspeed_sdc *sdc;
int ret;
@@ -529,6 +531,10 @@ static int aspeed_sdc_probe(struct platform_device *pdev)
spin_lock_init(&sdc->lock);
+ reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
+ if (IS_ERR(reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(reset), "unable to acquire reset\n");
+
sdc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sdc->clk))
return PTR_ERR(sdc->clk);
@@ -548,12 +554,11 @@ static int aspeed_sdc_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, sdc);
parent = pdev->dev.of_node;
- for_each_available_child_of_node(parent, child) {
+ for_each_available_child_of_node_scoped(parent, child) {
struct platform_device *cpdev;
cpdev = of_platform_device_create(child, NULL, &pdev->dev);
if (!cpdev) {
- of_node_put(child);
ret = -ENODEV;
goto err_clk;
}
diff --git a/drivers/mmc/host/sdhci-of-bst.c b/drivers/mmc/host/sdhci-of-bst.c
new file mode 100644
index 000000000000..f8d3df715e1a
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-bst.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SDHCI driver for Black Sesame Technologies C1200 controller
+ *
+ * Copyright (c) 2025 Black Sesame Technologies
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+/* SDHCI register extensions */
+#define SDHCI_CLOCK_PLL_EN 0x0008
+#define SDHCI_VENDOR_PTR_R 0xE8
+
+/* BST-specific tuning parameters */
+#define BST_TUNING_COUNT 0x20
+
+/* Synopsys vendor specific registers */
+#define SDHC_EMMC_CTRL_R_OFFSET 0x2C
+#define MBIU_CTRL 0x510
+
+/* MBIU burst control bits */
+#define BURST_INCR16_EN BIT(3)
+#define BURST_INCR8_EN BIT(2)
+#define BURST_INCR4_EN BIT(1)
+#define BURST_EN (BURST_INCR16_EN | BURST_INCR8_EN | BURST_INCR4_EN)
+#define MBIU_BURST_MASK GENMASK(3, 0)
+
+/* CRM (Clock/Reset/Management) register offsets */
+#define SDEMMC_CRM_BCLK_DIV_CTRL 0x08
+#define SDEMMC_CRM_TIMER_DIV_CTRL 0x0C
+#define SDEMMC_CRM_RX_CLK_CTRL 0x14
+#define SDEMMC_CRM_VOL_CTRL 0x1C
+#define REG_WR_PROTECT 0x88
+#define DELAY_CHAIN_SEL 0x94
+
+/* CRM register values and bit definitions */
+#define REG_WR_PROTECT_KEY 0x1234abcd
+#define BST_VOL_STABLE_ON BIT(7)
+#define BST_TIMER_DIV_MASK GENMASK(7, 0)
+#define BST_TIMER_DIV_VAL 0x20
+#define BST_TIMER_LOAD_BIT BIT(8)
+#define BST_BCLK_EN_BIT BIT(10)
+#define BST_RX_UPDATE_BIT BIT(11)
+#define BST_EMMC_CTRL_RST_N BIT(2) /* eMMC card reset control */
+
+/* Clock frequency limits */
+#define BST_DEFAULT_MAX_FREQ 200000000UL /* 200 MHz */
+#define BST_DEFAULT_MIN_FREQ 400000UL /* 400 kHz */
+
+/* Clock control bit definitions */
+#define BST_CLOCK_DIV_MASK GENMASK(7, 0)
+#define BST_CLOCK_DIV_SHIFT 8
+#define BST_BCLK_DIV_MASK GENMASK(9, 0)
+
+/* Clock frequency thresholds */
+#define BST_CLOCK_THRESHOLD_LOW 1500
+
+/* Clock stability polling parameters */
+#define BST_CLK_STABLE_POLL_US 1000 /* Poll interval in microseconds */
+#define BST_CLK_STABLE_TIMEOUT_US 20000 /* Timeout for internal clock stabilization (us) */
+
+struct sdhci_bst_priv {
+ void __iomem *crm_reg_base;
+};
+
+union sdhci_bst_rx_ctrl {
+ struct {
+ u32 rx_revert:1,
+ rx_clk_sel_sec:1,
+ rx_clk_div:4,
+ rx_clk_phase_inner:2,
+ rx_clk_sel_first:1,
+ rx_clk_phase_out:2,
+ rx_clk_en:1,
+ res0:20;
+ };
+ u32 reg;
+};
+
+static u32 sdhci_bst_crm_read(struct sdhci_pltfm_host *pltfm_host, u32 offset)
+{
+ struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ return readl(priv->crm_reg_base + offset);
+}
+
+static void sdhci_bst_crm_write(struct sdhci_pltfm_host *pltfm_host, u32 offset, u32 value)
+{
+ struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ writel(value, priv->crm_reg_base + offset);
+}
+
+static int sdhci_bst_wait_int_clk(struct sdhci_host *host)
+{
+ u16 clk;
+
+ if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE),
+ BST_CLK_STABLE_POLL_US, BST_CLK_STABLE_TIMEOUT_US, false,
+ host, SDHCI_CLOCK_CONTROL))
+ return -EBUSY;
+ return 0;
+}
+
+static unsigned int sdhci_bst_get_max_clock(struct sdhci_host *host)
+{
+ return BST_DEFAULT_MAX_FREQ;
+}
+
+static unsigned int sdhci_bst_get_min_clock(struct sdhci_host *host)
+{
+ return BST_DEFAULT_MIN_FREQ;
+}
+
+static void sdhci_bst_enable_clk(struct sdhci_host *host, unsigned int clk)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ unsigned int div;
+ u32 val;
+ union sdhci_bst_rx_ctrl rx_reg;
+
+ pltfm_host = sdhci_priv(host);
+
+ /* Calculate clock divider based on target frequency */
+ if (clk == 0) {
+ div = 0;
+ } else if (clk < BST_DEFAULT_MIN_FREQ) {
+ /* Below minimum: use max divider to get closest to min freq */
+ div = BST_DEFAULT_MAX_FREQ / BST_DEFAULT_MIN_FREQ;
+ } else if (clk <= BST_DEFAULT_MAX_FREQ) {
+ /* Normal range: calculate divider directly */
+ div = BST_DEFAULT_MAX_FREQ / clk;
+ } else {
+ /* Above maximum: no division needed */
+ div = 1;
+ }
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ clk &= ~SDHCI_CLOCK_PLL_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
+ val &= ~BST_TIMER_LOAD_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);
+
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
+ val &= ~BST_TIMER_DIV_MASK;
+ val |= BST_TIMER_DIV_VAL;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);
+
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
+ val |= BST_TIMER_LOAD_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);
+
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
+ val &= ~BST_RX_UPDATE_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val);
+
+ rx_reg.reg = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
+
+ rx_reg.rx_revert = 0;
+ rx_reg.rx_clk_sel_sec = 1;
+ rx_reg.rx_clk_div = 4;
+ rx_reg.rx_clk_phase_inner = 2;
+ rx_reg.rx_clk_sel_first = 0;
+ rx_reg.rx_clk_phase_out = 2;
+
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg);
+
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
+ val |= BST_RX_UPDATE_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val);
+
+ /* Disable clock first */
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
+ val &= ~BST_BCLK_EN_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+ /* Setup clock divider */
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
+ val &= ~BST_BCLK_DIV_MASK;
+ val |= div;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+ /* Enable clock */
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
+ val |= BST_BCLK_EN_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+ /* RMW the clock divider bits to avoid clobbering other fields */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~(BST_CLOCK_DIV_MASK << BST_CLOCK_DIV_SHIFT);
+ clk |= (div & BST_CLOCK_DIV_MASK) << BST_CLOCK_DIV_SHIFT;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_PLL_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+static void sdhci_bst_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ /* Turn off card/internal/PLL clocks when clock==0 to avoid idle power */
+ u32 clk_reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+ if (!clock) {
+ clk_reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN);
+ sdhci_writew(host, clk_reg, SDHCI_CLOCK_CONTROL);
+ return;
+ }
+ sdhci_bst_enable_clk(host, clock);
+}
+
+/*
+ * sdhci_bst_reset - Reset the SDHCI host controller with special
+ * handling for eMMC card reset control.
+ */
+static void sdhci_bst_reset(struct sdhci_host *host, u8 mask)
+{
+ u16 vendor_ptr, emmc_ctrl_reg;
+ u32 reg;
+
+ if (host->mmc->caps2 & MMC_CAP2_NO_SD) {
+ vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R);
+ emmc_ctrl_reg = vendor_ptr + SDHC_EMMC_CTRL_R_OFFSET;
+
+ reg = sdhci_readw(host, emmc_ctrl_reg);
+ reg &= ~BST_EMMC_CTRL_RST_N;
+ sdhci_writew(host, reg, emmc_ctrl_reg);
+ sdhci_reset(host, mask);
+ usleep_range(10, 20);
+ reg = sdhci_readw(host, emmc_ctrl_reg);
+ reg |= BST_EMMC_CTRL_RST_N;
+ sdhci_writew(host, reg, emmc_ctrl_reg);
+ } else {
+ sdhci_reset(host, mask);
+ }
+}
+
+/* Set timeout control register to maximum value (0xE) */
+static void sdhci_bst_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL);
+}
+
+/*
+ * sdhci_bst_set_power - Set power mode and voltage, also configures
+ * MBIU burst mode control based on power state.
+ */
+static void sdhci_bst_set_power(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ u32 reg;
+ u32 val;
+
+ sdhci_set_power(host, mode, vdd);
+
+ if (mode == MMC_POWER_OFF) {
+ /* Disable MBIU burst mode */
+ reg = sdhci_readw(host, MBIU_CTRL);
+ reg &= ~BURST_EN; /* Clear all burst enable bits */
+ sdhci_writew(host, reg, MBIU_CTRL);
+
+ /* Disable CRM BCLK */
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL);
+ val &= ~BST_BCLK_EN_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+ /* Disable RX clock */
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL);
+ val &= ~BST_RX_UPDATE_BIT;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val);
+
+ /* Turn off voltage stable power */
+ val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_VOL_CTRL);
+ val &= ~BST_VOL_STABLE_ON;
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, val);
+ } else {
+ /* Configure burst mode only when powered on */
+ reg = sdhci_readw(host, MBIU_CTRL);
+ reg &= ~MBIU_BURST_MASK; /* Clear burst related bits */
+ reg |= BURST_EN; /* Enable burst mode for better bandwidth */
+ sdhci_writew(host, reg, MBIU_CTRL);
+ }
+}
+
+/*
+ * sdhci_bst_execute_tuning - Execute tuning procedure by trying different
+ * delay chain values and selecting the optimal one.
+ */
+static int sdhci_bst_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ int ret = 0, error;
+ int first_start = -1, first_end = -1, best = 0;
+ int second_start = -1, second_end = -1, has_failure = 0;
+ int i;
+
+ pltfm_host = sdhci_priv(host);
+
+ for (i = 0; i < BST_TUNING_COUNT; i++) {
+ /* Protected write */
+ sdhci_bst_crm_write(pltfm_host, REG_WR_PROTECT, REG_WR_PROTECT_KEY);
+ /* Write tuning value */
+ sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << i) - 1);
+
+ /* Wait for internal clock stable before tuning */
+ if (sdhci_bst_wait_int_clk(host)) {
+ dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n");
+ return -EBUSY;
+ }
+
+ ret = mmc_send_tuning(host->mmc, opcode, &error);
+ if (ret != 0) {
+ has_failure = 1;
+ } else {
+ if (has_failure == 0) {
+ if (first_start == -1)
+ first_start = i;
+ first_end = i;
+ } else {
+ if (second_start == -1)
+ second_start = i;
+ second_end = i;
+ }
+ }
+ }
+
+ /* Calculate best tuning value */
+ if (first_end - first_start >= second_end - second_start)
+ best = ((first_end - first_start) >> 1) + first_start;
+ else
+ best = ((second_end - second_start) >> 1) + second_start;
+
+ if (best < 0)
+ best = 0;
+
+ sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << best) - 1);
+ /* Confirm internal clock stable after setting best tuning value */
+ if (sdhci_bst_wait_int_clk(host)) {
+ dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/* Enable voltage stable power for voltage switch */
+static void sdhci_bst_voltage_switch(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ /* Enable voltage stable power */
+ sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, BST_VOL_STABLE_ON);
+}
+
+static const struct sdhci_ops sdhci_bst_ops = {
+ .set_clock = sdhci_bst_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .get_min_clock = sdhci_bst_get_min_clock,
+ .get_max_clock = sdhci_bst_get_max_clock,
+ .reset = sdhci_bst_reset,
+ .set_power = sdhci_bst_set_power,
+ .set_timeout = sdhci_bst_set_timeout,
+ .platform_execute_tuning = sdhci_bst_execute_tuning,
+ .voltage_switch = sdhci_bst_voltage_switch,
+};
+
+static const struct sdhci_pltfm_data sdhci_bst_pdata = {
+ .ops = &sdhci_bst_ops,
+ .quirks = SDHCI_QUIRK_BROKEN_ADMA |
+ SDHCI_QUIRK_DELAY_AFTER_POWER |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT,
+ .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50 |
+ SDHCI_QUIRK2_TUNING_WORK_AROUND |
+ SDHCI_QUIRK2_ACMD23_BROKEN,
+};
+
+static void sdhci_bst_free_bounce_buffer(struct sdhci_host *host)
+{
+ if (host->bounce_buffer) {
+ dma_free_coherent(mmc_dev(host->mmc), host->bounce_buffer_size,
+ host->bounce_buffer, host->bounce_addr);
+ host->bounce_buffer = NULL;
+ }
+ of_reserved_mem_device_release(mmc_dev(host->mmc));
+}
+
+static int sdhci_bst_alloc_bounce_buffer(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ unsigned int bounce_size;
+ int ret;
+
+ /* Fixed SRAM bounce size to 32KB: verified config under 32-bit DMA addressing limit */
+ bounce_size = SZ_32K;
+
+ ret = of_reserved_mem_device_init_by_idx(mmc_dev(mmc), mmc_dev(mmc)->of_node, 0);
+ if (ret) {
+ dev_err(mmc_dev(mmc), "Failed to initialize reserved memory\n");
+ return ret;
+ }
+
+ host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size,
+ &host->bounce_addr, GFP_KERNEL);
+ if (!host->bounce_buffer) {
+ of_reserved_mem_device_release(mmc_dev(mmc));
+ return -ENOMEM;
+ }
+
+ host->bounce_buffer_size = bounce_size;
+
+ return 0;
+}
+
+static int sdhci_bst_probe(struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_host *host;
+ struct sdhci_bst_priv *priv;
+ int err;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_bst_pdata, sizeof(struct sdhci_bst_priv));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ priv = sdhci_pltfm_priv(pltfm_host); /* Get platform private data */
+
+ err = mmc_of_parse(host->mmc);
+ if (err)
+ return err;
+
+ sdhci_get_of_property(pdev);
+
+ /* Get CRM registers from the second reg entry */
+ priv->crm_reg_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(priv->crm_reg_base)) {
+ err = PTR_ERR(priv->crm_reg_base);
+ return err;
+ }
+
+ /*
+ * Silicon constraints for BST C1200:
+ * - System RAM base is 0x800000000 (above 32-bit addressable range)
+ * - The eMMC controller DMA engine is limited to 32-bit addressing
+ * - SMMU cannot be used on this path due to hardware design flaws
+ * - These are fixed in silicon and cannot be changed in software
+ *
+ * Bus/controller mapping:
+ * - No registers are available to reprogram the address mapping
+ * - The 32-bit DMA limit is a hard constraint of the controller IP
+ *
+ * Given these constraints, an SRAM-based bounce buffer in the 32-bit
+ * address space is required to enable eMMC DMA on this platform.
+ */
+ err = sdhci_bst_alloc_bounce_buffer(host);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to allocate bounce buffer: %d\n", err);
+ return err;
+ }
+
+ err = sdhci_add_host(host);
+ if (err)
+ goto err_free_bounce_buffer;
+
+ return 0;
+
+err_free_bounce_buffer:
+ sdhci_bst_free_bounce_buffer(host);
+
+ return err;
+}
+
+static void sdhci_bst_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+
+ sdhci_bst_free_bounce_buffer(host);
+ sdhci_pltfm_remove(pdev);
+}
+
+static const struct of_device_id sdhci_bst_ids[] = {
+ { .compatible = "bst,c1200-sdhci" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sdhci_bst_ids);
+
+static struct platform_driver sdhci_bst_driver = {
+ .driver = {
+ .name = "sdhci-bst",
+ .of_match_table = sdhci_bst_ids,
+ },
+ .probe = sdhci_bst_probe,
+ .remove = sdhci_bst_remove,
+};
+module_platform_driver(sdhci_bst_driver);
+
+MODULE_DESCRIPTION("Black Sesame Technologies SDHCI driver (BST)");
+MODULE_AUTHOR("Black Sesame Technologies Co., Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 2b75a36c096b..0b2158a7e409 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -40,7 +40,10 @@
#define DWCMSHC_AREA1_MASK GENMASK(11, 0)
/* Offset inside the vendor area 1 */
#define DWCMSHC_HOST_CTRL3 0x8
+#define DWCMSHC_HOST_CTRL3_CMD_CONFLICT BIT(0)
#define DWCMSHC_EMMC_CONTROL 0x2c
+/* HPE GSC SoC MSHCCS register */
+#define HPE_GSC_MSHCCS_SCGSYNCDIS BIT(18)
#define DWCMSHC_CARD_IS_EMMC BIT(0)
#define DWCMSHC_ENHANCED_STROBE BIT(8)
#define DWCMSHC_EMMC_ATCTRL 0x40
@@ -128,9 +131,11 @@
#define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */
#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */
#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */
+#define PHY_CNFG_PAD_SP_k230 0x09 /* PMOS TX drive strength for k230 */
#define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */
#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */
#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */
+#define PHY_CNFG_PAD_SN_k230 0x08 /* NMOS TX drive strength for k230 */
#define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */
/* PHY command/response pad settings */
@@ -153,14 +158,21 @@
#define PHY_PAD_RXSEL_3V3 0x2 /* Receiver type select for 3.3V */
#define PHY_PAD_WEAKPULL_MASK GENMASK(4, 3) /* bits [4:3] */
+#define PHY_PAD_WEAKPULL_DISABLED 0x0 /* Weak pull up and pull down disabled */
#define PHY_PAD_WEAKPULL_PULLUP 0x1 /* Weak pull up enabled */
#define PHY_PAD_WEAKPULL_PULLDOWN 0x2 /* Weak pull down enabled */
#define PHY_PAD_TXSLEW_CTRL_P_MASK GENMASK(8, 5) /* bits [8:5] */
#define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */
+#define PHY_PAD_TXSLEW_CTRL_P_k230 0x2 /* Slew control for P-Type pad TX for k230 */
#define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */
#define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */
#define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */
+#define PHY_PAD_TXSLEW_CTRL_N_k230 0x2 /* Slew control for N-Type pad TX for k230 */
+
+/* PHY Common DelayLine config settings */
+#define PHY_COMMDL_CNFG (DWC_MSHC_PTR_PHY_R + 0x1c)
+#define PHY_COMMDL_CNFG_DLSTEP_SEL BIT(0) /* DelayLine outputs on PAD enabled */
/* PHY CLK delay line settings */
#define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d)
@@ -174,7 +186,10 @@
#define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */
#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
+#define PHY_SMPLDL_CNFG_EXTDLY_EN BIT(0)
#define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1)
+#define PHY_SMPLDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */
+#define PHY_SMPLDL_CNFG_INPSEL 0x3 /* delay line input source */
/* PHY drift_cclk_rx delay line configuration setting */
#define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21)
@@ -224,9 +239,20 @@
SDHCI_TRNS_BLK_CNT_EN | \
SDHCI_TRNS_DMA)
+#define to_pltfm_data(priv, name) \
+ container_of((priv)->dwcmshc_pdata, struct name##_pltfm_data, dwcmshc_pdata)
+
/* SMC call for BlueField-3 eMMC RST_N */
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
+/* Canaan specific Registers */
+#define SD0_CTRL 0x00
+#define SD0_HOST_REG_VOL_STABLE BIT(4)
+#define SD0_CARD_WRITE_PROT BIT(6)
+#define SD1_CTRL 0x08
+#define SD1_HOST_REG_VOL_STABLE BIT(0)
+#define SD1_CARD_WRITE_PROT BIT(2)
+
/* Eswin specific Registers */
#define EIC7700_CARD_CLK_STABLE BIT(28)
#define EIC7700_INT_BCLK_STABLE BIT(16)
@@ -252,14 +278,8 @@
#define PHY_DELAY_CODE_EMMC 0x17
#define PHY_DELAY_CODE_SD 0x55
-enum dwcmshc_rk_type {
- DWCMSHC_RK3568,
- DWCMSHC_RK3588,
-};
-
struct rk35xx_priv {
struct reset_control *reset;
- enum dwcmshc_rk_type devtype;
u8 txclk_tapnum;
};
@@ -268,6 +288,11 @@ struct eic7700_priv {
unsigned int drive_impedance;
};
+struct k230_priv {
+ /* Canaan k230 specific */
+ struct regmap *hi_sys_regmap;
+};
+
#define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv {
@@ -278,6 +303,7 @@ struct dwcmshc_priv {
int num_other_clks;
struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS];
+ const struct dwcmshc_pltfm_data *dwcmshc_pdata;
void *priv; /* pointer to SoC private stuff */
u16 delay_line;
u16 flags;
@@ -290,6 +316,24 @@ struct dwcmshc_pltfm_data {
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
};
+struct k230_pltfm_data {
+ struct dwcmshc_pltfm_data dwcmshc_pdata;
+ bool is_emmc;
+ u32 ctrl_reg;
+ u32 vol_stable_bit;
+ u32 write_prot_bit;
+};
+
+struct rockchip_pltfm_data {
+ struct dwcmshc_pltfm_data dwcmshc_pdata;
+ /*
+ * The controller hardware has two known revisions documented internally:
+ * - Revision 0: Exclusively used by RK3566 and RK3568 SoCs.
+ * - Revision 1: Implemented in all other Rockchip SoCs, including RK3576, RK3588, etc.
+ */
+ int revision;
+};
+
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
{
u16 ctrl;
@@ -709,6 +753,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *priv = dwc_priv->priv;
+ const struct rockchip_pltfm_data *rockchip_pdata = to_pltfm_data(dwc_priv, rockchip);
u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
u32 extra, reg;
int err;
@@ -738,12 +783,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
extra |= BIT(4);
sdhci_writel(host, extra, reg);
+ /* Disable clock while config DLL */
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
if (clock <= 52000000) {
if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
dev_err(mmc_dev(host->mmc),
"Can't reduce the clock below 52MHz in HS200/HS400 mode");
- return;
+ goto enable_clk;
}
/*
@@ -763,7 +811,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
DLL_STRBIN_DELAY_NUM_SEL |
DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
- return;
+ goto enable_clk;
}
/* Reset DLL */
@@ -776,7 +824,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
* we must set it in higher speed mode.
*/
extra = DWCMSHC_EMMC_DLL_DLYENA;
- if (priv->devtype == DWCMSHC_RK3568)
+ if (rockchip_pdata->revision == 0)
extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
@@ -790,7 +838,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
500 * USEC_PER_MSEC);
if (err) {
dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n");
- return;
+ goto enable_clk;
}
extra = 0x1 << 16 | /* tune clock stop en */
@@ -802,7 +850,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
txclk_tapnum = priv->txclk_tapnum;
- if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
+ if (rockchip_pdata->revision == 1 && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES;
extra = DLL_CMDOUT_SRC_CLK_NEG |
@@ -823,6 +871,16 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
DLL_STRBIN_TAPNUM_DEFAULT |
DLL_STRBIN_TAPNUM_FROM_SW;
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
+
+enable_clk:
+ /*
+ * The sdclk frequency select bits in SDHCI_CLOCK_CONTROL are not functional
+ * on Rockchip's SDHCI implementation. Instead, the clock frequency is fully
+ * controlled via external clk provider by calling clk_set_rate(). Consequently,
+ * passing 0 to sdhci_enable_clk() only re-enables the already-configured clock,
+ * which matches the hardware's actual behavior.
+ */
+ sdhci_enable_clk(host, 0);
}
static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
@@ -858,11 +916,6 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
if (!priv)
return -ENOMEM;
- if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc"))
- priv->devtype = DWCMSHC_RK3588;
- else
- priv->devtype = DWCMSHC_RK3568;
-
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
if (IS_ERR(priv->reset)) {
err = PTR_ERR(priv->reset);
@@ -1245,6 +1298,126 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
ARRAY_SIZE(clk_ids), clk_ids);
}
+/*
+ * HPE GSC-specific vendor configuration: disable command conflict check
+ * and program Auto-Tuning Control register.
+ */
+static void dwcmshc_hpe_vendor_specific(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 atctrl;
+ u8 extra;
+
+ extra = sdhci_readb(host, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
+ extra &= ~DWCMSHC_HOST_CTRL3_CMD_CONFLICT;
+ sdhci_writeb(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
+
+ atctrl = AT_CTRL_AT_EN | AT_CTRL_SWIN_TH_EN | AT_CTRL_TUNE_CLK_STOP_EN |
+ FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, 3) |
+ FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY) |
+ FIELD_PREP(AT_CTRL_SWIN_TH_VAL_MASK, 2);
+ sdhci_writel(host, atctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+}
+
+static void dwcmshc_hpe_set_emmc(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u16 ctrl;
+
+ ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+ ctrl |= DWCMSHC_CARD_IS_EMMC;
+ sdhci_writew(host, ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+}
+
+static void dwcmshc_hpe_reset(struct sdhci_host *host, u8 mask)
+{
+ dwcmshc_reset(host, mask);
+ dwcmshc_hpe_vendor_specific(host);
+ dwcmshc_hpe_set_emmc(host);
+}
+
+static void dwcmshc_hpe_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ dwcmshc_set_uhs_signaling(host, timing);
+ dwcmshc_hpe_set_emmc(host);
+}
+
+/*
+ * HPE GSC eMMC controller clock setup.
+ *
+ * The GSC SoC wires the freq_sel field of SDHCI_CLOCK_CONTROL directly to a
+ * clock mux rather than a divider. Force freq_sel = 1 when running at
+ * 200 MHz (HS200) so the mux selects the correct clock source.
+ */
+static void dwcmshc_hpe_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ u16 clk;
+
+ host->mmc->actual_clock = 0;
+
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+ return;
+
+ clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
+
+ if (host->mmc->actual_clock == 200000000)
+ clk |= (1 << SDHCI_DIVIDER_SHIFT);
+
+ sdhci_enable_clk(host, clk);
+}
+
+/*
+ * HPE GSC eMMC controller init.
+ *
+ * The GSC SoC requires configuring MSHCCS. Bit 18 (SCGSyncDis) disables clock
+ * synchronisation for phase-select values going to the HS200 RX delay lines,
+ * allowing the card clock to be stopped while the delay selection settles and
+ * the phase shift is applied. This must be used together with the ATCTRL
+ * settings programmed in dwcmshc_hpe_vendor_specific():
+ * AT_CTRL_R.TUNE_CLK_STOP_EN = 0x1
+ * AT_CTRL_R.POST_CHANGE_DLY = 0x3
+ * AT_CTRL_R.PRE_CHANGE_DLY = 0x3
+ *
+ * The DTS node provides a syscon phandle ('hpe,gxp-sysreg') with the
+ * MSHCCS register offset as an argument.
+ */
+static int dwcmshc_hpe_gsc_init(struct device *dev, struct sdhci_host *host,
+ struct dwcmshc_priv *dwc_priv)
+{
+ unsigned int reg_offset;
+ struct regmap *soc_ctrl;
+ int ret;
+
+ /* Disable cmd conflict check and configure auto-tuning */
+ dwcmshc_hpe_vendor_specific(host);
+
+ /* Look up the GXP sysreg syscon and MSHCCS offset */
+ soc_ctrl = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "hpe,gxp-sysreg",
+ 1, &reg_offset);
+ if (IS_ERR(soc_ctrl)) {
+ dev_err(dev, "failed to get hpe,gxp-sysreg syscon\n");
+ return PTR_ERR(soc_ctrl);
+ }
+
+ /* Set SCGSyncDis (bit 18) to disable sync on HS200 RX delay lines */
+ ret = regmap_update_bits(soc_ctrl, reg_offset,
+ HPE_GSC_MSHCCS_SCGSYNCDIS,
+ HPE_GSC_MSHCCS_SCGSYNCDIS);
+ if (ret) {
+ dev_err(dev, "failed to set SCGSyncDis in MSHCCS\n");
+ return ret;
+ }
+
+ sdhci_enable_v4_mode(host);
+
+ return 0;
+}
+
static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1656,6 +1829,181 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm
return 0;
}
+static void dwcmshc_k230_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ u16 clk;
+
+ sdhci_set_clock(host, clock);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ /*
+ * It is necessary to enable SDHCI_PROG_CLOCK_MODE. This is a
+ * vendor-specific quirk. If this is not done, the eMMC will be
+ * unable to read or write.
+ */
+ clk |= SDHCI_PROG_CLOCK_MODE;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+static void sdhci_k230_config_phy_delay(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 val;
+
+ sdhci_writeb(host, PHY_COMMDL_CNFG_DLSTEP_SEL, PHY_COMMDL_CNFG);
+ sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
+ sdhci_writeb(host, PHY_SDCLKDL_DC_INITIAL, PHY_SDCLKDL_DC_R);
+
+ val = PHY_SMPLDL_CNFG_EXTDLY_EN;
+ val |= FIELD_PREP(PHY_SMPLDL_CNFG_INPSEL_MASK, PHY_SMPLDL_CNFG_INPSEL);
+ sdhci_writeb(host, val, PHY_SMPLDL_CNFG_R);
+
+ sdhci_writeb(host, FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL),
+ PHY_ATDL_CNFG_R);
+
+ val = sdhci_readl(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+ val |= AT_CTRL_TUNE_CLK_STOP_EN;
+ val |= FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, AT_CTRL_PRE_CHANGE_DLY);
+ val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY);
+ sdhci_writel(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+ sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+}
+
+static int dwcmshc_k230_phy_init(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 rxsel;
+ u32 val;
+ u32 reg;
+ int ret;
+
+ /* reset phy */
+ sdhci_writew(host, 0, PHY_CNFG_R);
+
+ /* Disable the clock */
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ rxsel = dwc_priv->flags & FLAG_IO_FIXED_1V8 ? PHY_PAD_RXSEL_1V8 : PHY_PAD_RXSEL_3V3;
+
+ val = rxsel;
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230);
+ val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
+
+ sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+
+ val = rxsel;
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230);
+ sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+
+ val = rxsel;
+ val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230);
+ sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+
+ sdhci_k230_config_phy_delay(host);
+
+ /* Wait max 150 ms */
+ ret = read_poll_timeout(sdhci_readl, reg,
+ (reg & FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1)),
+ 10, 150000, false, host, PHY_CNFG_R);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc), "READ PHY PWRGOOD timeout!\n");
+ return -ETIMEDOUT;
+ }
+
+ reg = FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_k230) |
+ FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_k230);
+ sdhci_writel(host, reg, PHY_CNFG_R);
+
+ /* de-assert the phy */
+ reg |= PHY_CNFG_RSTN_DEASSERT;
+ sdhci_writel(host, reg, PHY_CNFG_R);
+
+ return 0;
+}
+
+static void dwcmshc_k230_sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230);
+ u8 emmc_ctrl;
+
+ dwcmshc_reset(host, mask);
+
+ if (mask != SDHCI_RESET_ALL)
+ return;
+
+ emmc_ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+ sdhci_writeb(host, emmc_ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+
+ if (k230_pdata->is_emmc)
+ dwcmshc_k230_phy_init(host);
+ else
+ sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
+}
+
+static int dwcmshc_k230_init(struct device *dev, struct sdhci_host *host,
+ struct dwcmshc_priv *dwc_priv)
+{
+ const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230);
+ static const char * const clk_ids[] = {"block", "timer", "axi"};
+ struct device_node *usb_phy_node;
+ struct k230_priv *k230_priv;
+ u32 data;
+ int ret;
+
+ k230_priv = devm_kzalloc(dev, sizeof(struct k230_priv), GFP_KERNEL);
+ if (!k230_priv)
+ return -ENOMEM;
+
+ dwc_priv->priv = k230_priv;
+
+ usb_phy_node = of_parse_phandle(dev->of_node, "canaan,usb-phy", 0);
+ if (!usb_phy_node)
+ return dev_err_probe(dev, -ENODEV, "Failed to find canaan,usb-phy phandle\n");
+
+ k230_priv->hi_sys_regmap = device_node_to_regmap(usb_phy_node);
+ of_node_put(usb_phy_node);
+
+ if (IS_ERR(k230_priv->hi_sys_regmap))
+ return dev_err_probe(dev, PTR_ERR(k230_priv->hi_sys_regmap),
+ "Failed to get k230-usb-phy regmap\n");
+
+ ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
+ ARRAY_SIZE(clk_ids), clk_ids);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get/enable k230 mmc other clocks\n");
+
+ if (k230_pdata->is_emmc) {
+ host->flags &= ~SDHCI_SIGNALING_330;
+ dwc_priv->flags |= FLAG_IO_FIXED_1V8;
+ } else {
+ host->mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+ host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+ }
+
+ ret = regmap_read(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, &data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read control reg 0x%x\n",
+ k230_pdata->ctrl_reg);
+
+ data |= k230_pdata->write_prot_bit | k230_pdata->vol_stable_bit;
+ ret = regmap_write(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to write control reg 0x%x\n",
+ k230_pdata->ctrl_reg);
+
+ return 0;
+}
+
static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
@@ -1743,6 +2091,15 @@ static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
.platform_execute_tuning = sdhci_eic7700_executing_tuning,
};
+static const struct sdhci_ops sdhci_dwcmshc_k230_ops = {
+ .set_clock = dwcmshc_k230_sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = dwcmshc_set_uhs_signaling,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .reset = dwcmshc_k230_sdhci_reset,
+ .adma_write_desc = dwcmshc_adma_write_desc,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_ops,
@@ -1771,30 +2128,52 @@ static const struct cqhci_host_ops rk35xx_cqhci_ops = {
.set_tran_desc = dwcmshc_set_tran_desc,
};
-static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
- .pdata = {
- .ops = &sdhci_dwcmshc_rk35xx_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
- SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3568_pdata = {
+ .dwcmshc_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk35xx_postinit,
},
- .cqhci_host_ops = &rk35xx_cqhci_ops,
- .init = dwcmshc_rk35xx_init,
- .postinit = dwcmshc_rk35xx_postinit,
+ .revision = 0,
};
-static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
- .pdata = {
- .ops = &sdhci_dwcmshc_rk35xx_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
- SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
+ .dwcmshc_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk3576_postinit,
},
- .cqhci_host_ops = &rk35xx_cqhci_ops,
- .init = dwcmshc_rk35xx_init,
- .postinit = dwcmshc_rk3576_postinit,
+ .revision = 1,
+};
+
+static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3588_pdata = {
+ .dwcmshc_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk35xx_postinit,
+ },
+ .revision = 1,
};
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
@@ -1834,6 +2213,55 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
.init = eic7700_init,
};
+static const struct k230_pltfm_data k230_emmc_data = {
+ .dwcmshc_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_k230_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
+ },
+ .init = dwcmshc_k230_init,
+ },
+ .is_emmc = true,
+ .ctrl_reg = SD0_CTRL,
+ .vol_stable_bit = SD0_HOST_REG_VOL_STABLE,
+ .write_prot_bit = SD0_CARD_WRITE_PROT,
+};
+
+static const struct k230_pltfm_data k230_sdio_data = {
+ .dwcmshc_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_k230_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
+ },
+ .init = dwcmshc_k230_init,
+ },
+ .is_emmc = false,
+ .ctrl_reg = SD1_CTRL,
+ .vol_stable_bit = SD1_HOST_REG_VOL_STABLE,
+ .write_prot_bit = SD1_CARD_WRITE_PROT,
+};
+
+static const struct sdhci_ops sdhci_dwcmshc_hpe_ops = {
+ .set_clock = dwcmshc_hpe_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = dwcmshc_hpe_set_uhs_signaling,
+ .get_max_clock = dwcmshc_get_max_clock,
+ .reset = dwcmshc_hpe_reset,
+ .adma_write_desc = dwcmshc_adma_write_desc,
+ .irq = dwcmshc_cqe_irq_handler,
+};
+
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_hpe_gsc_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_hpe_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+ .init = dwcmshc_hpe_gsc_init,
+};
+
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.enable = dwcmshc_sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
@@ -1907,8 +2335,16 @@ dsbl_cqe_caps:
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
{
+ .compatible = "canaan,k230-emmc",
+ .data = &k230_emmc_data.dwcmshc_pdata,
+ },
+ {
+ .compatible = "canaan,k230-sdio",
+ .data = &k230_sdio_data.dwcmshc_pdata,
+ },
+ {
.compatible = "rockchip,rk3588-dwcmshc",
- .data = &sdhci_dwcmshc_rk35xx_pdata,
+ .data = &sdhci_dwcmshc_rk3588_pdata,
},
{
.compatible = "rockchip,rk3576-dwcmshc",
@@ -1916,7 +2352,7 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
},
{
.compatible = "rockchip,rk3568-dwcmshc",
- .data = &sdhci_dwcmshc_rk35xx_pdata,
+ .data = &sdhci_dwcmshc_rk3568_pdata,
},
{
.compatible = "snps,dwcmshc-sdhci",
@@ -1942,6 +2378,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "eswin,eic7700-dwcmshc",
.data = &sdhci_dwcmshc_eic7700_pdata,
},
+ {
+ .compatible = "hpe,gsc-dwcmshc",
+ .data = &sdhci_dwcmshc_hpe_gsc_pdata,
+ },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@@ -1988,6 +2428,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
priv = sdhci_pltfm_priv(pltfm_host);
+ priv->dwcmshc_pdata = pltfm_data;
if (dev->of_node) {
pltfm_host->clk = devm_clk_get(dev, "core");
diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c
index 0cc97e23a2f9..455656f9842d 100644
--- a/drivers/mmc/host/sdhci-of-k1.c
+++ b/drivers/mmc/host/sdhci-of-k1.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/reset.h>
#include <linux/platform_device.h>
#include "sdhci.h"
@@ -223,6 +224,21 @@ static inline int spacemit_sdhci_get_clocks(struct device *dev,
return 0;
}
+static inline int spacemit_sdhci_get_resets(struct device *dev)
+{
+ struct reset_control *rst;
+
+ rst = devm_reset_control_get_optional_shared_deasserted(dev, "axi");
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+
+ rst = devm_reset_control_get_optional_exclusive_deasserted(dev, "sdh");
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+
+ return 0;
+}
+
static const struct sdhci_ops spacemit_sdhci_ops = {
.get_max_clock = spacemit_sdhci_clk_get_max_clock,
.reset = spacemit_sdhci_reset,
@@ -243,8 +259,20 @@ static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
+static const struct sdhci_pltfm_data spacemit_sdhci_k3_pdata = {
+ .ops = &spacemit_sdhci_ops,
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+ SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
static const struct of_device_id spacemit_sdhci_of_match[] = {
- { .compatible = "spacemit,k1-sdhci" },
+ { .compatible = "spacemit,k1-sdhci", .data = &spacemit_sdhci_k1_pdata },
+ { .compatible = "spacemit,k3-sdhci", .data = &spacemit_sdhci_k3_pdata },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match);
@@ -255,10 +283,13 @@ static int spacemit_sdhci_probe(struct platform_device *pdev)
struct spacemit_sdhci_host *sdhst;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_host *host;
+ const struct sdhci_pltfm_data *data;
struct mmc_host_ops *mops;
int ret;
- host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst));
+ data = of_device_get_match_data(&pdev->dev);
+
+ host = sdhci_pltfm_init(pdev, data, sizeof(*sdhst));
if (IS_ERR(host))
return PTR_ERR(host);
@@ -284,6 +315,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev)
if (ret)
goto err_pltfm;
+ ret = spacemit_sdhci_get_resets(dev);
+ if (ret)
+ goto err_pltfm;
+
ret = sdhci_add_host(host);
if (ret)
goto err_pltfm;
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 47a0a738862b..c347fac24515 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -20,7 +20,6 @@
#include <linux/scatterlist.h>
#include <linux/io.h>
#include <linux/iopoll.h>
-#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
index 2cc632e91fe4..6899cf6c65e7 100644
--- a/drivers/mmc/host/sdhci-pic32.c
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Support of SDHCI platform devices for Microchip PIC32.
*
@@ -5,10 +6,6 @@
* Andrei Pistirica, Paul Thacker
*
* Inspired by sdhci-pltfm.c
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 7f6ac636f040..933fafe0a0ef 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -95,13 +95,6 @@ void sdhci_get_property(struct platform_device *pdev)
sdhci_get_compatibility(pdev);
device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock);
-
- if (device_property_present(dev, "keep-power-in-suspend"))
- host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
-
- if (device_property_read_bool(dev, "wakeup-source") ||
- device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
- host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
EXPORT_SYMBOL_GPL(sdhci_get_property);
@@ -215,19 +208,6 @@ const struct dev_pm_ops sdhci_pltfm_pmops = {
};
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
-static int __init sdhci_pltfm_drv_init(void)
-{
- pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n");
-
- return 0;
-}
-module_init(sdhci_pltfm_drv_init);
-
-static void __exit sdhci_pltfm_drv_exit(void)
-{
-}
-module_exit(sdhci_pltfm_drv_exit);
-
MODULE_DESCRIPTION("SDHCI platform and OF driver helper");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index c459a08d01da..41e49c6cc751 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -1126,7 +1126,7 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id)
/*****************************************************************************\
* *
- * Driver init/exit *
+ * Driver init *
* *
\*****************************************************************************/
@@ -1138,17 +1138,6 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host)
return 0;
}
-static int __init sdhci_uhs2_mod_init(void)
-{
- return 0;
-}
-module_init(sdhci_uhs2_mod_init);
-
-static void __exit sdhci_uhs2_mod_exit(void)
-{
-}
-module_exit(sdhci_uhs2_mod_exit);
-
/*****************************************************************************\
*
* Device allocation/registration *
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fec9329e1edb..605be55f8d2d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -4193,6 +4193,12 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host)
unsigned int bounce_size;
int ret;
+ /* Drivers may have already allocated the buffer */
+ if (host->bounce_buffer) {
+ bounce_size = host->bounce_buffer_size;
+ max_blocks = bounce_size / 512;
+ goto out;
+ }
/*
* Cap the bounce buffer at 64KB. Using a bigger bounce buffer
* has diminishing returns, this is probably because SD/MMC
@@ -4241,6 +4247,7 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host)
host->bounce_buffer_size = bounce_size;
+out:
/* Lie about this since we're bouncing */
mmc->max_segs = max_blocks;
mmc->max_seg_size = bounce_size;
@@ -5004,22 +5011,6 @@ EXPORT_SYMBOL_GPL(sdhci_remove_host);
* *
\*****************************************************************************/
-static int __init sdhci_drv_init(void)
-{
- pr_info(DRIVER_NAME
- ": Secure Digital Host Controller Interface driver\n");
- pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
-
- return 0;
-}
-
-static void __exit sdhci_drv_exit(void)
-{
-}
-
-module_init(sdhci_drv_init);
-module_exit(sdhci_drv_exit);
-
module_param(debug_quirks, uint, 0444);
module_param(debug_quirks2, uint, 0444);
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index 2cd69c9e9571..c1f7d5b37911 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -193,9 +193,7 @@ static void tifm_sd_transfer_data(struct tifm_sd *host)
pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT);
p_off = offset_in_page(off);
- p_cnt = PAGE_SIZE - p_off;
- p_cnt = min(p_cnt, cnt);
- p_cnt = min(p_cnt, t_size);
+ p_cnt = min3(PAGE_SIZE - p_off, cnt, t_size);
if (r_data->flags & MMC_DATA_READ)
tifm_sd_read_fifo(host, pg, p_off, p_cnt);
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 3c9df27f9fa7..6c3cb2f1c9d3 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -2107,19 +2107,19 @@ static int vub300_probe(struct usb_interface *interface,
command_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!command_out_urb) {
retval = -ENOMEM;
- goto error0;
+ goto err_put_udev;
}
command_res_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!command_res_urb) {
retval = -ENOMEM;
- goto error1;
+ goto err_free_out_urb;
}
/* this also allocates memory for our VUB300 mmc host device */
mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev);
if (!mmc) {
retval = -ENOMEM;
dev_err(&udev->dev, "not enough memory for the mmc_host\n");
- goto error4;
+ goto err_free_res_urb;
}
/* MMC core transfer sizes tunable parameters */
mmc->caps = 0;
@@ -2336,10 +2336,11 @@ static int vub300_probe(struct usb_interface *interface,
interface_to_InterfaceNumber(interface));
retval = mmc_add_host(mmc);
if (retval)
- goto error6;
+ goto err_delete_timer;
return 0;
-error6:
+
+err_delete_timer:
timer_delete_sync(&vub300->inactivity_timer);
err_free_host:
mmc_free_host(mmc);
@@ -2347,12 +2348,13 @@ err_free_host:
* and hence also frees vub300
* which is contained at the end of struct mmc
*/
-error4:
+err_free_res_urb:
usb_free_urb(command_res_urb);
-error1:
+err_free_out_urb:
usb_free_urb(command_out_urb);
-error0:
+err_put_udev:
usb_put_dev(udev);
+
return retval;
}
@@ -2427,37 +2429,36 @@ static int __init vub300_init(void)
pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X",
firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout);
+
cmndworkqueue = create_singlethread_workqueue("kvub300c");
- if (!cmndworkqueue) {
- pr_err("not enough memory for the REQUEST workqueue");
- result = -ENOMEM;
- goto out1;
- }
+ if (!cmndworkqueue)
+ return -ENOMEM;
+
pollworkqueue = create_singlethread_workqueue("kvub300p");
if (!pollworkqueue) {
- pr_err("not enough memory for the IRQPOLL workqueue");
result = -ENOMEM;
- goto out2;
+ goto err_destroy_cmdwq;
}
+
deadworkqueue = create_singlethread_workqueue("kvub300d");
if (!deadworkqueue) {
- pr_err("not enough memory for the EXPIRED workqueue");
result = -ENOMEM;
- goto out3;
+ goto err_destroy_pollwq;
}
+
result = usb_register(&vub300_driver);
- if (result) {
- pr_err("usb_register failed. Error number %d", result);
- goto out4;
- }
+ if (result)
+ goto err_destroy_deadwq;
+
return 0;
-out4:
+
+err_destroy_deadwq:
destroy_workqueue(deadworkqueue);
-out3:
+err_destroy_pollwq:
destroy_workqueue(pollworkqueue);
-out2:
+err_destroy_cmdwq:
destroy_workqueue(cmndworkqueue);
-out1:
+
return result;
}
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
index c68132e38138..6d17dfa25dad 100644
--- a/drivers/mux/Kconfig
+++ b/drivers/mux/Kconfig
@@ -4,10 +4,21 @@
#
config MULTIPLEXER
- tristate
+ bool
+
+config MUX_CORE
+ bool "Generic Multiplexer Support"
+ select MULTIPLEXER
+ help
+ This framework is designed to abstract multiplexer handling for
+ devices via various GPIO-, MMIO/Regmap or specific multiplexer
+ controller chips.
+
+ If unsure, say no.
+
+if MULTIPLEXER
menu "Multiplexer drivers"
- depends on MULTIPLEXER
config MUX_ADG792A
tristate "Analog Devices ADG792A/ADG792G Multiplexers"
@@ -60,3 +71,5 @@ config MUX_MMIO
be called mux-mmio.
endmenu
+
+endif # MULTIPLEXER
diff --git a/drivers/mux/core.c b/drivers/mux/core.c
index f09ee8782e3d..23538de2c91b 100644
--- a/drivers/mux/core.c
+++ b/drivers/mux/core.c
@@ -46,6 +46,16 @@ static const struct class mux_class = {
.name = "mux",
};
+/**
+ * struct devm_mux_state_state - Tracks managed resources for mux-state objects.
+ * @mstate: Pointer to a mux state.
+ * @exit: An optional callback to execute before free.
+ */
+struct devm_mux_state_state {
+ struct mux_state *mstate;
+ int (*exit)(struct mux_state *mstate);
+};
+
static DEFINE_IDA(mux_ida);
static int __init mux_init(void)
@@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
return dev ? to_mux_chip(dev) : NULL;
}
-/*
+/**
* mux_get() - Get the mux-control for a device.
* @dev: The device that needs a mux-control.
* @mux_name: The name identifying the mux-control.
* @state: Pointer to where the requested state is returned, or NULL when
* the required multiplexer states are handled by other means.
+ * @optional: Whether to return NULL and silence errors when mux doesn't exist.
*
- * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
+ * Return: Pointer to the mux-control on success, an ERR_PTR with a negative
+ * errno on error, or NULL if optional is true and mux doesn't exist.
*/
static struct mux_control *mux_get(struct device *dev, const char *mux_name,
- unsigned int *state)
+ unsigned int *state, bool optional)
{
struct device_node *np = dev->of_node;
struct of_phandle_args args;
@@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
else
index = of_property_match_string(np, "mux-control-names",
mux_name);
- if (index < 0) {
+ if (index < 0 && optional) {
+ return NULL;
+ } else if (index < 0) {
dev_err(dev, "mux controller '%s' not found\n",
mux_name);
return ERR_PTR(index);
@@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
"mux-controls", "#mux-control-cells",
index, &args);
if (ret) {
+ if (optional && ret == -ENOENT)
+ return NULL;
+
dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
- np, state ? "state" : "control", mux_name ?: "", index);
+ np, state ? "state" : "control",
+ mux_name ?: "", index);
return ERR_PTR(ret);
}
@@ -617,11 +635,30 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
*/
struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
{
- return mux_get(dev, mux_name, NULL);
+ struct mux_control *mux = mux_get(dev, mux_name, NULL, false);
+
+ if (!mux)
+ return ERR_PTR(-ENOENT);
+
+ return mux;
}
EXPORT_SYMBOL_GPL(mux_control_get);
/**
+ * mux_control_get_optional() - Get the optional mux-control for a device.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: Pointer to the mux-control on success, an ERR_PTR with a negative
+ * errno on error, or NULL if mux doesn't exist.
+ */
+struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name)
+{
+ return mux_get(dev, mux_name, NULL, true);
+}
+EXPORT_SYMBOL_GPL(mux_control_get_optional);
+
+/**
* mux_control_put() - Put away the mux-control for good.
* @mux: The mux-control to put away.
*
@@ -670,14 +707,16 @@ struct mux_control *devm_mux_control_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_mux_control_get);
-/*
+/**
* mux_state_get() - Get the mux-state for a device.
* @dev: The device that needs a mux-state.
* @mux_name: The name identifying the mux-state.
+ * @optional: Whether to return NULL and silence errors when mux doesn't exist.
*
- * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno.
+ * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
+ * errno on error, or NULL if optional is true and mux doesn't exist.
*/
-static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
+static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional)
{
struct mux_state *mstate;
@@ -685,12 +724,15 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
if (!mstate)
return ERR_PTR(-ENOMEM);
- mstate->mux = mux_get(dev, mux_name, &mstate->state);
+ mstate->mux = mux_get(dev, mux_name, &mstate->state, optional);
if (IS_ERR(mstate->mux)) {
int err = PTR_ERR(mstate->mux);
kfree(mstate);
return ERR_PTR(err);
+ } else if (!mstate->mux) {
+ kfree(mstate);
+ return optional ? NULL : ERR_PTR(-ENOENT);
}
return mstate;
@@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate)
static void devm_mux_state_release(struct device *dev, void *res)
{
- struct mux_state *mstate = *(struct mux_state **)res;
+ struct devm_mux_state_state *devm_state = res;
+
+ if (devm_state->exit)
+ devm_state->exit(devm_state->mstate);
+
+ mux_state_put(devm_state->mstate);
+}
+
+/**
+ * __devm_mux_state_get() - Get the optional mux-state for a device,
+ * with resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ * @optional: Whether to return NULL and silence errors when mux doesn't exist.
+ * @init: Optional function pointer for mux-state object initialisation.
+ * @exit: Optional function pointer for mux-state object cleanup on release.
+ *
+ * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
+ * errno on error, or NULL if optional is true and mux doesn't exist.
+ */
+static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name,
+ bool optional,
+ int (*init)(struct mux_state *mstate),
+ int (*exit)(struct mux_state *mstate))
+{
+ struct devm_mux_state_state *devm_state;
+ struct mux_state *mstate;
+ int ret;
+
+ mstate = mux_state_get(dev, mux_name, optional);
+ if (IS_ERR(mstate))
+ return ERR_CAST(mstate);
+ else if (optional && !mstate)
+ return NULL;
+ else if (!mstate)
+ return ERR_PTR(-ENOENT);
+
+ devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL);
+ if (!devm_state) {
+ ret = -ENOMEM;
+ goto err_devres_alloc;
+ }
+
+ if (init) {
+ ret = init(mstate);
+ if (ret)
+ goto err_mux_state_init;
+ }
+
+ devm_state->mstate = mstate;
+ devm_state->exit = exit;
+ devres_add(dev, devm_state);
+ return mstate;
+
+err_mux_state_init:
+ devres_free(devm_state);
+err_devres_alloc:
mux_state_put(mstate);
+ return ERR_PTR(ret);
}
/**
@@ -722,28 +821,69 @@ static void devm_mux_state_release(struct device *dev, void *res)
* @mux_name: The name identifying the mux-control.
*
* Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
+ *
+ * The mux-state will automatically be freed on release.
*/
-struct mux_state *devm_mux_state_get(struct device *dev,
- const char *mux_name)
+struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
{
- struct mux_state **ptr, *mstate;
-
- ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
+ return __devm_mux_state_get(dev, mux_name, false, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get);
- mstate = mux_state_get(dev, mux_name);
- if (IS_ERR(mstate)) {
- devres_free(ptr);
- return mstate;
- }
+/**
+ * devm_mux_state_get_optional() - Get the optional mux-state for a device,
+ * with resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
+ * errno on error, or NULL if mux doesn't exist.
+ *
+ * The mux-state will automatically be freed on release.
+ */
+struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name)
+{
+ return __devm_mux_state_get(dev, mux_name, true, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
- *ptr = mstate;
- devres_add(dev, ptr);
+/**
+ * devm_mux_state_get_selected() - Get the mux-state for a device, with
+ * resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
+ *
+ * The returned mux-state (if valid) is already selected.
+ *
+ * The mux-state will automatically be deselected and freed on release.
+ */
+struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name)
+{
+ return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect);
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
- return mstate;
+/**
+ * devm_mux_state_get_optional_selected() - Get the optional mux-state for
+ * a device, with resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
+ * errno on error, or NULL if mux doesn't exist.
+ *
+ * The returned mux-state (if valid) is already selected.
+ *
+ * The mux-state will automatically be deselected and freed on release.
+ */
+struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
+ const char *mux_name)
+{
+ return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect);
}
-EXPORT_SYMBOL_GPL(devm_mux_state_get);
+EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected);
/*
* Using subsys_initcall instead of module_init here to try to ensure - for
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index 330356706ad7..2b52e47f247a 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = {
};
MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids);
-/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
-static inline struct mux_state *
-devm_mux_state_get_optional(struct device *dev, const char *mux_name)
-{
- if (!of_property_present(dev->of_node, "mux-states"))
- return NULL;
-
- return devm_mux_state_get(dev, mux_name);
-}
-
static struct phy *can_transceiver_phy_xlate(struct device *dev,
const struct of_phandle_args *args)
{
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index cfc2a8d9028d..79e820e2fe55 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha
return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable);
}
-/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
-static inline struct mux_state *
-devm_mux_state_get_optional(struct device *dev, const char *mux_name)
-{
- if (!of_property_present(dev->of_node, "mux-states"))
- return NULL;
-
- return devm_mux_state_get(dev, mux_name);
-}
-
-static void rcar_gen3_phy_mux_state_deselect(void *data)
-{
- mux_state_deselect(data);
-}
-
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
}
- mux_state = devm_mux_state_get_optional(dev, NULL);
+ mux_state = devm_mux_state_get_optional_selected(dev, NULL);
if (IS_ERR(mux_state))
- return PTR_ERR(mux_state);
- if (mux_state) {
- ret = mux_state_select(mux_state);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to select USB mux\n");
-
- ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect,
- mux_state);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to register USB mux state deselect\n");
- }
+ return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n");
if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) {
ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel);