diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 14:15:25 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 14:15:25 -0700 |
| commit | 4ddd4f0651a710f33dfbb9dadd94f2bb0aa31aa8 (patch) | |
| tree | e1d5912e626588751347c425a54a3936fdf3f6e1 /drivers | |
| parent | b9962335d4c6dee152e95dce9f0dd32048735a6d (diff) | |
| parent | 52957cdad30f8011da1f4ef1338ba0339ca4c158 (diff) | |
| download | lwn-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')
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, ®s); 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, ®_offset); of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_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, ®_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); |
