Lines Matching +full:mmc +full:-

1 // SPDX-License-Identifier: GPL-2.0-only
3 * Loongson-2K MMC/SDIO controller driver
5 * Copyright (C) 2018-2025 Loongson Technology Corporation Limited.
14 #include <linux/dma-mapping.h>
17 #include <linux/mmc/core.h>
18 #include <linux/mmc/host.h>
19 #include <linux/mmc/mmc.h>
20 #include <linux/mmc/sd.h>
21 #include <linux/mmc/sdio.h>
22 #include <linux/mmc/slot-gpio.h>
48 #define LOONGSON2_MMC_REG_DLLVAL 0xf0 /* DLL Master Lock-value Register */
154 /* Bitfields of DLL master lock-value register */
192 /* Loongson-2K1000 SDIO2 DMA routing register */
200 /* Loongson-2K0500 SDIO2 DMA routing register */
261 if (cmd->data) in loongson2_mmc_send_command()
262 host->state = STATE_XFERFINISH_RSPFIN; in loongson2_mmc_send_command()
263 else if (cmd->flags & MMC_RSP_PRESENT) in loongson2_mmc_send_command()
264 host->state = STATE_RSPFIN; in loongson2_mmc_send_command()
266 host->state = STATE_CMDSENT; in loongson2_mmc_send_command()
268 regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, cmd->arg); in loongson2_mmc_send_command()
270 cctrl = FIELD_PREP(LOONGSON2_MMC_CCTL_INDEX, cmd->opcode); in loongson2_mmc_send_command()
273 if (cmd->opcode == SD_SWITCH && cmd->data) in loongson2_mmc_send_command()
276 if (cmd->flags & MMC_RSP_PRESENT) in loongson2_mmc_send_command()
279 if (cmd->flags & MMC_RSP_136) in loongson2_mmc_send_command()
282 regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, cctrl); in loongson2_mmc_send_command()
290 if ((data->blksz & 3) != 0) in loongson2_mmc_setup_data()
291 return -EINVAL; in loongson2_mmc_setup_data()
293 dctrl = FIELD_PREP(LOONGSON2_MMC_DCTL_BNUM, data->blocks); in loongson2_mmc_setup_data()
296 if (host->bus_width == MMC_BUS_WIDTH_4) in loongson2_mmc_setup_data()
298 else if (host->bus_width == MMC_BUS_WIDTH_8) in loongson2_mmc_setup_data()
301 regmap_write(host->regmap, LOONGSON2_MMC_REG_DCTL, dctrl); in loongson2_mmc_setup_data()
302 regmap_write(host->regmap, LOONGSON2_MMC_REG_BSIZE, data->blksz); in loongson2_mmc_setup_data()
303 regmap_write(host->regmap, LOONGSON2_MMC_REG_TIMER, U32_MAX); in loongson2_mmc_setup_data()
320 host->dma_complete = 0; in loongson2_mmc_prepare_dma()
322 return host->pdata->prepare_dma(host, data); in loongson2_mmc_prepare_dma()
325 static void loongson2_mmc_send_request(struct mmc_host *mmc) in loongson2_mmc_send_request() argument
328 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_send_request()
329 struct mmc_request *mrq = host->mrq; in loongson2_mmc_send_request()
330 struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; in loongson2_mmc_send_request()
332 ret = loongson2_mmc_prepare_dma(host, cmd->data); in loongson2_mmc_send_request()
334 dev_err(host->dev, "DMA data prepared failed with %d\n", ret); in loongson2_mmc_send_request()
335 cmd->error = ret; in loongson2_mmc_send_request()
336 cmd->data->error = ret; in loongson2_mmc_send_request()
337 mmc_request_done(mmc, mrq); in loongson2_mmc_send_request()
341 if (host->pdata->fix_data_timeout) in loongson2_mmc_send_request()
342 host->pdata->fix_data_timeout(host, cmd); in loongson2_mmc_send_request()
347 if (cmd->opcode == MMC_SELECT_CARD && cmd->arg == 0) { in loongson2_mmc_send_request()
348 cmd->error = 0; in loongson2_mmc_send_request()
349 mmc_request_done(mmc, mrq); in loongson2_mmc_send_request()
356 struct mmc_host *mmc = mmc_from_priv(host); in loongson2_mmc_irq_worker() local
357 struct mmc_request *mrq = host->mrq; in loongson2_mmc_irq_worker()
358 struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; in loongson2_mmc_irq_worker()
360 if (cmd->data) in loongson2_mmc_irq_worker()
361 dma_unmap_sg(mmc_dev(mmc), cmd->data->sg, cmd->data->sg_len, in loongson2_mmc_irq_worker()
362 mmc_get_dma_dir(cmd->data)); in loongson2_mmc_irq_worker()
364 if (cmd->data && !cmd->error && in loongson2_mmc_irq_worker()
365 !cmd->data->error && !host->dma_complete) in loongson2_mmc_irq_worker()
369 regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP0, &cmd->resp[0]); in loongson2_mmc_irq_worker()
370 regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP1, &cmd->resp[1]); in loongson2_mmc_irq_worker()
371 regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP2, &cmd->resp[2]); in loongson2_mmc_irq_worker()
372 regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP3, &cmd->resp[3]); in loongson2_mmc_irq_worker()
375 regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, 0); in loongson2_mmc_irq_worker()
376 regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, 0); in loongson2_mmc_irq_worker()
378 if (cmd->data && cmd->error) in loongson2_mmc_irq_worker()
379 cmd->data->error = cmd->error; in loongson2_mmc_irq_worker()
381 if (cmd->data && cmd->data->stop && !host->cmd_is_stop) { in loongson2_mmc_irq_worker()
382 host->cmd_is_stop = 1; in loongson2_mmc_irq_worker()
383 loongson2_mmc_send_request(mmc); in loongson2_mmc_irq_worker()
388 if (!mrq->data) in loongson2_mmc_irq_worker()
392 if (mrq->data->error == 0) { in loongson2_mmc_irq_worker()
393 mrq->data->bytes_xfered = in loongson2_mmc_irq_worker()
394 (mrq->data->blocks * mrq->data->blksz); in loongson2_mmc_irq_worker()
396 mrq->data->bytes_xfered = 0; in loongson2_mmc_irq_worker()
400 host->state = STATE_NONE; in loongson2_mmc_irq_worker()
401 host->mrq = NULL; in loongson2_mmc_irq_worker()
402 mmc_request_done(mmc, mrq); in loongson2_mmc_irq_worker()
409 struct mmc_host *mmc = mmc_from_priv(host); in loongson2_mmc_irq() local
414 regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk); in loongson2_mmc_irq()
415 regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts); in loongson2_mmc_irq()
419 regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT, in loongson2_mmc_irq()
422 sdio_signal_irq(mmc); in loongson2_mmc_irq()
426 spin_lock_irqsave(&host->lock, iflags); in loongson2_mmc_irq()
428 if (host->state == STATE_NONE || host->state == STATE_FINALIZE || !host->mrq) in loongson2_mmc_irq()
431 cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; in loongson2_mmc_irq()
435 cmd->error = 0; in loongson2_mmc_irq()
438 cmd->error = -ETIMEDOUT; in loongson2_mmc_irq()
443 if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT) in loongson2_mmc_irq()
446 if (host->state == STATE_XFERFINISH_RSPFIN) in loongson2_mmc_irq()
447 host->state = STATE_XFERFINISH; in loongson2_mmc_irq()
450 if (!cmd->data) in loongson2_mmc_irq()
454 cmd->data->error = -EILSEQ; in loongson2_mmc_irq()
459 cmd->data->error = -ETIMEDOUT; in loongson2_mmc_irq()
464 if (host->state == STATE_XFERFINISH) { in loongson2_mmc_irq()
465 host->dma_complete = 1; in loongson2_mmc_irq()
469 if (host->state == STATE_XFERFINISH_RSPFIN) in loongson2_mmc_irq()
470 host->state = STATE_RSPFIN; in loongson2_mmc_irq()
474 regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk); in loongson2_mmc_irq()
475 spin_unlock_irqrestore(&host->lock, iflags); in loongson2_mmc_irq()
479 host->state = STATE_FINALIZE; in loongson2_mmc_irq()
480 host->pdata->reorder_cmd_data(host, cmd); in loongson2_mmc_irq()
481 regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk); in loongson2_mmc_irq()
482 spin_unlock_irqrestore(&host->lock, iflags); in loongson2_mmc_irq()
491 regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_SEL, in loongson2_mmc_dll_mode_init()
501 regmap_write(host->regmap, LOONGSON2_MMC_REG_DLLCTL, val); in loongson2_mmc_dll_mode_init()
503 ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_DLLVAL, val, in loongson2_mmc_dll_mode_init()
509 regmap_read(host->regmap, LOONGSON2_MMC_REG_DLLVAL, &val); in loongson2_mmc_dll_mode_init()
515 regmap_write(host->regmap, LOONGSON2_MMC_REG_DELAY, delay); in loongson2_mmc_dll_mode_init()
522 pre = DIV_ROUND_UP(host->current_clk, ios->clock); in loongson2_mmc_set_clk()
526 regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN); in loongson2_mmc_set_clk()
528 regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL, in loongson2_mmc_set_clk()
532 if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52) in loongson2_mmc_set_clk()
536 static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) in loongson2_mmc_set_ios() argument
538 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_set_ios()
541 if (ios->power_mode == MMC_POWER_UP) { in loongson2_mmc_set_ios()
542 if (!IS_ERR(mmc->supply.vmmc)) { in loongson2_mmc_set_ios()
543 ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); in loongson2_mmc_set_ios()
545 dev_err(host->dev, "failed to enable vmmc regulator\n"); in loongson2_mmc_set_ios()
549 regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_RESET); in loongson2_mmc_set_ios()
551 regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_EXTCLK); in loongson2_mmc_set_ios()
552 regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, LOONGSON2_MMC_IEN_ALL); in loongson2_mmc_set_ios()
553 regmap_write(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_CLEAR); in loongson2_mmc_set_ios()
554 } else if (ios->power_mode == MMC_POWER_OFF) { in loongson2_mmc_set_ios()
555 regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL, in loongson2_mmc_set_ios()
557 if (!IS_ERR(mmc->supply.vmmc)) in loongson2_mmc_set_ios()
558 mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); in loongson2_mmc_set_ios()
564 host->bus_width = ios->bus_width; in loongson2_mmc_set_ios()
567 static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) in loongson2_mmc_request() argument
569 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_request()
571 host->cmd_is_stop = 0; in loongson2_mmc_request()
572 host->mrq = mrq; in loongson2_mmc_request()
573 loongson2_mmc_send_request(mmc); in loongson2_mmc_request()
576 static void loongson2_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) in loongson2_mmc_enable_sdio_irq() argument
578 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_enable_sdio_irq()
580 regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_SDIOIRQ, enable); in loongson2_mmc_enable_sdio_irq()
583 static void loongson2_mmc_ack_sdio_irq(struct mmc_host *mmc) in loongson2_mmc_ack_sdio_irq() argument
585 loongson2_mmc_enable_sdio_irq(mmc, 1); in loongson2_mmc_ack_sdio_irq()
624 if (cmd->opcode == loongson2_reorder_cmd_list[i]) in ls2k0500_mmc_reorder_cmd_data()
630 for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { in ls2k0500_mmc_reorder_cmd_data()
633 if (cmd->opcode == SD_SWITCH) in ls2k0500_mmc_reorder_cmd_data()
643 struct mmc_host *mmc = mmc_from_priv(host); in loongson2_mmc_prepare_external_dma() local
648 ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, in loongson2_mmc_prepare_external_dma()
651 return -ENOMEM; in loongson2_mmc_prepare_external_dma()
653 dma_conf.src_addr = host->res->start + LOONGSON2_MMC_REG_DATA, in loongson2_mmc_prepare_external_dma()
654 dma_conf.dst_addr = host->res->start + LOONGSON2_MMC_REG_DATA, in loongson2_mmc_prepare_external_dma()
657 dma_conf.direction = !(data->flags & MMC_DATA_WRITE) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; in loongson2_mmc_prepare_external_dma()
659 dmaengine_slave_config(host->chan, &dma_conf); in loongson2_mmc_prepare_external_dma()
660 desc = dmaengine_prep_slave_sg(host->chan, data->sg, data->sg_len, in loongson2_mmc_prepare_external_dma()
667 dma_async_issue_pending(host->chan); in loongson2_mmc_prepare_external_dma()
672 dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, mmc_get_dma_dir(data)); in loongson2_mmc_prepare_external_dma()
673 return -ENOMEM; in loongson2_mmc_prepare_external_dma()
679 dma_release_channel(host->chan); in loongson2_mmc_release_external_dma()
696 host->chan = dma_request_chan(&pdev->dev, "rx-tx"); in ls2k0500_mmc_set_external_dma()
697 ret = PTR_ERR_OR_ZERO(host->chan); in ls2k0500_mmc_set_external_dma()
699 dev_err(&pdev->dev, "Cannot get DMA channel.\n"); in ls2k0500_mmc_set_external_dma()
728 host->chan = dma_request_chan(&pdev->dev, "rx-tx"); in ls2k1000_mmc_set_external_dma()
729 ret = PTR_ERR_OR_ZERO(host->chan); in ls2k1000_mmc_set_external_dma()
731 dev_err(&pdev->dev, "Cannot get DMA channel.\n"); in ls2k1000_mmc_set_external_dma()
760 if (cmd->opcode != SD_SWITCH || mmc_cmd_type(cmd) != MMC_CMD_ADTC) in ls2k2000_mmc_reorder_cmd_data()
763 for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { in ls2k2000_mmc_reorder_cmd_data()
780 if (cmd->opcode != MMC_WRITE_BLOCK && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) in ls2k2000_mmc_fix_data_timeout()
783 regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_FSTS, val, in ls2k2000_mmc_fix_data_timeout()
791 struct loongson2_dma_desc *pdes = (struct loongson2_dma_desc *)host->sg_cpu; in loongson2_mmc_prepare_internal_dma()
792 struct mmc_host *mmc = mmc_from_priv(host); in loongson2_mmc_prepare_internal_dma() local
793 dma_addr_t next_desc = host->sg_dma; in loongson2_mmc_prepare_internal_dma()
799 ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, in loongson2_mmc_prepare_internal_dma()
802 return -ENOMEM; in loongson2_mmc_prepare_internal_dma()
804 for_each_sg(data->sg, sg, data->sg_len, i) { in loongson2_mmc_prepare_internal_dma()
810 pdes[i].apb_addr = host->res->start + LOONGSON2_MMC_REG_DATA; in loongson2_mmc_prepare_internal_dma()
813 if (data->flags & MMC_DATA_READ) { in loongson2_mmc_prepare_internal_dma()
829 pdes[i - 1].ndesc_addr &= ~LOONGSON2_MMC_DMA_DESC_EN; in loongson2_mmc_prepare_internal_dma()
831 dma_order = (host->sg_dma & ~LOONGSON2_MMC_DMA_CONFIG_MASK) | in loongson2_mmc_prepare_internal_dma()
835 regmap_write(host->regmap, reg_hi, upper_32_bits(dma_order)); in loongson2_mmc_prepare_internal_dma()
836 regmap_write(host->regmap, reg_lo, lower_32_bits(dma_order)); in loongson2_mmc_prepare_internal_dma()
844 host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, in ls2k2000_mmc_set_internal_dma()
845 &host->sg_dma, GFP_KERNEL); in ls2k2000_mmc_set_internal_dma()
846 if (!host->sg_cpu) in ls2k2000_mmc_set_internal_dma()
847 return -ENOMEM; in ls2k2000_mmc_set_internal_dma()
849 memset(host->sg_cpu, 0, PAGE_SIZE); in ls2k2000_mmc_set_internal_dma()
856 dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); in loongson2_mmc_release_internal_dma()
871 struct device *dev = &pdev->dev; in loongson2_mmc_resource_request()
875 base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res); in loongson2_mmc_resource_request()
879 host->regmap = devm_regmap_init_mmio(dev, base, host->pdata->regmap_config); in loongson2_mmc_resource_request()
880 if (IS_ERR(host->regmap)) in loongson2_mmc_resource_request()
881 return PTR_ERR(host->regmap); in loongson2_mmc_resource_request()
883 host->clk = devm_clk_get_optional_enabled(dev, NULL); in loongson2_mmc_resource_request()
884 if (IS_ERR(host->clk)) in loongson2_mmc_resource_request()
885 return PTR_ERR(host->clk); in loongson2_mmc_resource_request()
887 if (host->clk) { in loongson2_mmc_resource_request()
888 ret = devm_clk_rate_exclusive_get(dev, host->clk); in loongson2_mmc_resource_request()
892 host->current_clk = clk_get_rate(host->clk); in loongson2_mmc_resource_request()
894 /* For ACPI, the clock is accessed via the clock-frequency attribute. */ in loongson2_mmc_resource_request()
895 device_property_read_u32(dev, "clock-frequency", &host->current_clk); in loongson2_mmc_resource_request()
904 IRQF_ONESHOT, "loongson2-mmc", host); in loongson2_mmc_resource_request()
912 return host->pdata->setting_dma(host, pdev); in loongson2_mmc_resource_request()
917 struct device *dev = &pdev->dev; in loongson2_mmc_probe()
919 struct mmc_host *mmc; in loongson2_mmc_probe() local
922 mmc = devm_mmc_alloc_host(dev, sizeof(*host)); in loongson2_mmc_probe()
923 if (!mmc) in loongson2_mmc_probe()
924 return -ENOMEM; in loongson2_mmc_probe()
926 platform_set_drvdata(pdev, mmc); in loongson2_mmc_probe()
928 host = mmc_priv(mmc); in loongson2_mmc_probe()
929 host->state = STATE_NONE; in loongson2_mmc_probe()
930 spin_lock_init(&host->lock); in loongson2_mmc_probe()
932 host->pdata = device_get_match_data(dev); in loongson2_mmc_probe()
933 if (!host->pdata) in loongson2_mmc_probe()
934 return dev_err_probe(dev, -EINVAL, "Failed to get match data\n"); in loongson2_mmc_probe()
940 mmc->ops = &loongson2_mmc_ops; in loongson2_mmc_probe()
941 mmc->f_min = DIV_ROUND_UP(host->current_clk, 256); in loongson2_mmc_probe()
942 mmc->f_max = host->current_clk; in loongson2_mmc_probe()
943 mmc->max_blk_count = 4095; in loongson2_mmc_probe()
944 mmc->max_blk_size = 4095; in loongson2_mmc_probe()
945 mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; in loongson2_mmc_probe()
946 mmc->max_segs = 1; in loongson2_mmc_probe()
947 mmc->max_seg_size = mmc->max_req_size; in loongson2_mmc_probe()
950 if (mmc->caps & MMC_CAP_SDIO_IRQ) in loongson2_mmc_probe()
951 mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; in loongson2_mmc_probe()
953 ret = mmc_regulator_get_supply(mmc); in loongson2_mmc_probe()
954 if (ret || mmc->ocr_avail == 0) { in loongson2_mmc_probe()
956 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; in loongson2_mmc_probe()
959 ret = mmc_of_parse(mmc); in loongson2_mmc_probe()
965 ret = mmc_add_host(mmc); in loongson2_mmc_probe()
967 dev_err(dev, "Failed to add mmc host\n"); in loongson2_mmc_probe()
974 host->pdata->release_dma(host, dev); in loongson2_mmc_probe()
980 struct mmc_host *mmc = platform_get_drvdata(pdev); in loongson2_mmc_remove() local
981 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_remove()
983 mmc_remove_host(mmc); in loongson2_mmc_remove()
984 host->pdata->release_dma(host, &pdev->dev); in loongson2_mmc_remove()
988 { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
989 { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
990 { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata },
997 struct mmc_host *mmc = dev_get_drvdata(dev); in loongson2_mmc_suspend() local
998 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_suspend()
1000 clk_disable_unprepare(host->clk); in loongson2_mmc_suspend()
1007 struct mmc_host *mmc = dev_get_drvdata(dev); in loongson2_mmc_resume() local
1008 struct loongson2_mmc_host *host = mmc_priv(mmc); in loongson2_mmc_resume()
1010 return clk_prepare_enable(host->clk); in loongson2_mmc_resume()
1017 .name = "loongson2-mmc",
1028 MODULE_DESCRIPTION("Loongson-2K SD/SDIO/eMMC Interface driver");