Lines Matching +full:mmc +full:- +full:host
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * meson-mx-sdio.c - Meson6, Meson8 and Meson8b SDIO/MMC Host Controller
12 #include <linux/clk-provider.h>
15 #include <linux/dma-mapping.h>
25 #include <linux/mmc/host.h>
26 #include <linux/mmc/mmc.h>
27 #include <linux/mmc/sdio.h>
28 #include <linux/mmc/slot-gpio.h>
98 #define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
118 struct mmc_host *mmc; member
125 static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask, in meson_mx_mmc_mask_bits() argument
128 struct meson_mx_mmc_host *host = mmc_priv(mmc); in meson_mx_mmc_mask_bits() local
131 regval = readl(host->base + reg); in meson_mx_mmc_mask_bits()
135 writel(regval, host->base + reg); in meson_mx_mmc_mask_bits()
138 static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host) in meson_mx_mmc_soft_reset() argument
140 writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC); in meson_mx_mmc_soft_reset()
146 if (cmd->opcode == MMC_SET_BLOCK_COUNT && !cmd->error) in meson_mx_mmc_get_next_cmd()
147 return cmd->mrq->cmd; in meson_mx_mmc_get_next_cmd()
148 else if (mmc_op_multi(cmd->opcode) && in meson_mx_mmc_get_next_cmd()
149 (!cmd->mrq->sbc || cmd->error || cmd->data->error)) in meson_mx_mmc_get_next_cmd()
150 return cmd->mrq->stop; in meson_mx_mmc_get_next_cmd()
155 static void meson_mx_mmc_start_cmd(struct mmc_host *mmc, in meson_mx_mmc_start_cmd() argument
158 struct meson_mx_mmc_host *host = mmc_priv(mmc); in meson_mx_mmc_start_cmd() local
163 host->cmd = cmd; in meson_mx_mmc_start_cmd()
165 if (cmd->busy_timeout) in meson_mx_mmc_start_cmd()
166 timeout = msecs_to_jiffies(cmd->busy_timeout); in meson_mx_mmc_start_cmd()
174 /* 7 (CMD) + 32 (response) + 7 (CRC) -1 */ in meson_mx_mmc_start_cmd()
178 /* 7 (CMD) + 120 (response) + 7 (CRC) -1 */ in meson_mx_mmc_start_cmd()
186 if (!(cmd->flags & MMC_RSP_CRC)) in meson_mx_mmc_start_cmd()
189 if (cmd->flags & MMC_RSP_BUSY) in meson_mx_mmc_start_cmd()
192 if (cmd->data) { in meson_mx_mmc_start_cmd()
194 (cmd->data->blocks - 1)); in meson_mx_mmc_start_cmd()
196 pack_size = cmd->data->blksz * BITS_PER_BYTE; in meson_mx_mmc_start_cmd()
197 if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) in meson_mx_mmc_start_cmd()
205 if (cmd->data->flags & MMC_DATA_WRITE) in meson_mx_mmc_start_cmd()
210 cmd->data->bytes_xfered = 0; in meson_mx_mmc_start_cmd()
214 (0x40 | cmd->opcode)); in meson_mx_mmc_start_cmd()
216 spin_lock_irqsave(&host->irq_lock, irqflags); in meson_mx_mmc_start_cmd()
218 mult = readl(host->base + MESON_MX_SDIO_MULT); in meson_mx_mmc_start_cmd()
220 mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id); in meson_mx_mmc_start_cmd()
222 writel(mult, host->base + MESON_MX_SDIO_MULT); in meson_mx_mmc_start_cmd()
225 meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC, in meson_mx_mmc_start_cmd()
230 meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS, in meson_mx_mmc_start_cmd()
234 writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU); in meson_mx_mmc_start_cmd()
235 writel(ext, host->base + MESON_MX_SDIO_EXT); in meson_mx_mmc_start_cmd()
236 writel(send, host->base + MESON_MX_SDIO_SEND); in meson_mx_mmc_start_cmd()
238 spin_unlock_irqrestore(&host->irq_lock, irqflags); in meson_mx_mmc_start_cmd()
240 mod_timer(&host->cmd_timeout, jiffies + timeout); in meson_mx_mmc_start_cmd()
243 static void meson_mx_mmc_request_done(struct meson_mx_mmc_host *host) in meson_mx_mmc_request_done() argument
247 mrq = host->mrq; in meson_mx_mmc_request_done()
249 if (host->cmd->error) in meson_mx_mmc_request_done()
250 meson_mx_mmc_soft_reset(host); in meson_mx_mmc_request_done()
252 host->mrq = NULL; in meson_mx_mmc_request_done()
253 host->cmd = NULL; in meson_mx_mmc_request_done()
255 mmc_request_done(host->mmc, mrq); in meson_mx_mmc_request_done()
258 static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) in meson_mx_mmc_set_ios() argument
260 struct meson_mx_mmc_host *host = mmc_priv(mmc); in meson_mx_mmc_set_ios() local
261 unsigned short vdd = ios->vdd; in meson_mx_mmc_set_ios()
262 unsigned long clk_rate = ios->clock; in meson_mx_mmc_set_ios()
264 switch (ios->bus_width) { in meson_mx_mmc_set_ios()
266 meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF, in meson_mx_mmc_set_ios()
271 meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF, in meson_mx_mmc_set_ios()
278 dev_err(mmc_dev(mmc), "unsupported bus width: %d\n", in meson_mx_mmc_set_ios()
279 ios->bus_width); in meson_mx_mmc_set_ios()
280 host->error = -EINVAL; in meson_mx_mmc_set_ios()
284 host->error = clk_set_rate(host->cfg_div_clk, ios->clock); in meson_mx_mmc_set_ios()
285 if (host->error) { in meson_mx_mmc_set_ios()
286 dev_warn(mmc_dev(mmc), in meson_mx_mmc_set_ios()
287 "failed to set MMC clock to %lu: %d\n", in meson_mx_mmc_set_ios()
288 clk_rate, host->error); in meson_mx_mmc_set_ios()
292 mmc->actual_clock = clk_get_rate(host->cfg_div_clk); in meson_mx_mmc_set_ios()
294 switch (ios->power_mode) { in meson_mx_mmc_set_ios()
299 if (!IS_ERR(mmc->supply.vmmc)) { in meson_mx_mmc_set_ios()
300 host->error = mmc_regulator_set_ocr(mmc, in meson_mx_mmc_set_ios()
301 mmc->supply.vmmc, in meson_mx_mmc_set_ios()
303 if (host->error) in meson_mx_mmc_set_ios()
310 static int meson_mx_mmc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq) in meson_mx_mmc_map_dma() argument
312 struct mmc_data *data = mrq->data; in meson_mx_mmc_map_dma()
319 sg = data->sg; in meson_mx_mmc_map_dma()
320 if (sg->offset & 3 || sg->length & 3) { in meson_mx_mmc_map_dma()
321 dev_err(mmc_dev(mmc), in meson_mx_mmc_map_dma()
323 sg->offset, sg->length); in meson_mx_mmc_map_dma()
324 return -EINVAL; in meson_mx_mmc_map_dma()
327 dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, in meson_mx_mmc_map_dma()
330 dev_err(mmc_dev(mmc), "dma_map_sg failed\n"); in meson_mx_mmc_map_dma()
331 return -ENOMEM; in meson_mx_mmc_map_dma()
337 static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) in meson_mx_mmc_request() argument
339 struct meson_mx_mmc_host *host = mmc_priv(mmc); in meson_mx_mmc_request() local
340 struct mmc_command *cmd = mrq->cmd; in meson_mx_mmc_request()
342 if (!host->error) in meson_mx_mmc_request()
343 host->error = meson_mx_mmc_map_dma(mmc, mrq); in meson_mx_mmc_request()
345 if (host->error) { in meson_mx_mmc_request()
346 cmd->error = host->error; in meson_mx_mmc_request()
347 mmc_request_done(mmc, mrq); in meson_mx_mmc_request()
351 host->mrq = mrq; in meson_mx_mmc_request()
353 if (mrq->data) in meson_mx_mmc_request()
354 writel(sg_dma_address(mrq->data->sg), in meson_mx_mmc_request()
355 host->base + MESON_MX_SDIO_ADDR); in meson_mx_mmc_request()
357 if (mrq->sbc) in meson_mx_mmc_request()
358 meson_mx_mmc_start_cmd(mmc, mrq->sbc); in meson_mx_mmc_request()
360 meson_mx_mmc_start_cmd(mmc, mrq->cmd); in meson_mx_mmc_request()
363 static void meson_mx_mmc_read_response(struct mmc_host *mmc, in meson_mx_mmc_read_response() argument
366 struct meson_mx_mmc_host *host = mmc_priv(mmc); in meson_mx_mmc_read_response() local
370 mult = readl(host->base + MESON_MX_SDIO_MULT); in meson_mx_mmc_read_response()
374 writel(mult, host->base + MESON_MX_SDIO_MULT); in meson_mx_mmc_read_response()
376 if (cmd->flags & MMC_RSP_136) { in meson_mx_mmc_read_response()
378 resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU); in meson_mx_mmc_read_response()
379 cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff); in meson_mx_mmc_read_response()
380 cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff); in meson_mx_mmc_read_response()
381 cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff); in meson_mx_mmc_read_response()
382 cmd->resp[3] = (resp[3] << 8); in meson_mx_mmc_read_response()
383 } else if (cmd->flags & MMC_RSP_PRESENT) { in meson_mx_mmc_read_response()
384 cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU); in meson_mx_mmc_read_response()
388 static irqreturn_t meson_mx_mmc_process_cmd_irq(struct meson_mx_mmc_host *host, in meson_mx_mmc_process_cmd_irq() argument
391 struct mmc_command *cmd = host->cmd; in meson_mx_mmc_process_cmd_irq()
401 cmd->error = 0; in meson_mx_mmc_process_cmd_irq()
402 meson_mx_mmc_read_response(host->mmc, cmd); in meson_mx_mmc_process_cmd_irq()
404 if (cmd->data) { in meson_mx_mmc_process_cmd_irq()
407 cmd->error = -EILSEQ; in meson_mx_mmc_process_cmd_irq()
411 cmd->error = -EILSEQ; in meson_mx_mmc_process_cmd_irq()
419 struct meson_mx_mmc_host *host = (void *) data; in meson_mx_mmc_irq() local
423 spin_lock(&host->irq_lock); in meson_mx_mmc_irq()
425 irqs = readl(host->base + MESON_MX_SDIO_IRQS); in meson_mx_mmc_irq()
426 send = readl(host->base + MESON_MX_SDIO_SEND); in meson_mx_mmc_irq()
429 ret = meson_mx_mmc_process_cmd_irq(host, irqs, send); in meson_mx_mmc_irq()
434 writel(irqs, host->base + MESON_MX_SDIO_IRQS); in meson_mx_mmc_irq()
436 spin_unlock(&host->irq_lock); in meson_mx_mmc_irq()
443 struct meson_mx_mmc_host *host = (void *) irq_data; in meson_mx_mmc_irq_thread() local
444 struct mmc_command *cmd = host->cmd, *next_cmd; in meson_mx_mmc_irq_thread()
449 del_timer_sync(&host->cmd_timeout); in meson_mx_mmc_irq_thread()
451 if (cmd->data) { in meson_mx_mmc_irq_thread()
452 dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg, in meson_mx_mmc_irq_thread()
453 cmd->data->sg_len, in meson_mx_mmc_irq_thread()
454 mmc_get_dma_dir(cmd->data)); in meson_mx_mmc_irq_thread()
456 cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks; in meson_mx_mmc_irq_thread()
461 meson_mx_mmc_start_cmd(host->mmc, next_cmd); in meson_mx_mmc_irq_thread()
463 meson_mx_mmc_request_done(host); in meson_mx_mmc_irq_thread()
470 struct meson_mx_mmc_host *host = from_timer(host, t, cmd_timeout); in meson_mx_mmc_timeout() local
474 spin_lock_irqsave(&host->irq_lock, irqflags); in meson_mx_mmc_timeout()
477 irqc = readl(host->base + MESON_MX_SDIO_IRQC); in meson_mx_mmc_timeout()
479 writel(irqc, host->base + MESON_MX_SDIO_IRQC); in meson_mx_mmc_timeout()
481 spin_unlock_irqrestore(&host->irq_lock, irqflags); in meson_mx_mmc_timeout()
487 if (!host->cmd) in meson_mx_mmc_timeout()
490 dev_dbg(mmc_dev(host->mmc), in meson_mx_mmc_timeout()
492 host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS), in meson_mx_mmc_timeout()
493 readl(host->base + MESON_MX_SDIO_ARGU)); in meson_mx_mmc_timeout()
495 host->cmd->error = -ETIMEDOUT; in meson_mx_mmc_timeout()
497 meson_mx_mmc_request_done(host); in meson_mx_mmc_timeout()
513 * TODO: the MMC core framework currently does not support in meson_mx_mmc_slot_pdev()
517 slot_node = of_get_compatible_child(parent->of_node, "mmc-slot"); in meson_mx_mmc_slot_pdev()
519 dev_warn(parent, "no 'mmc-slot' sub-node found\n"); in meson_mx_mmc_slot_pdev()
520 return ERR_PTR(-ENOENT); in meson_mx_mmc_slot_pdev()
529 static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host) in meson_mx_mmc_add_host() argument
531 struct mmc_host *mmc = host->mmc; in meson_mx_mmc_add_host() local
532 struct device *slot_dev = mmc_dev(mmc); in meson_mx_mmc_add_host()
535 if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) { in meson_mx_mmc_add_host()
537 return -EINVAL; in meson_mx_mmc_add_host()
540 if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) { in meson_mx_mmc_add_host()
542 host->slot_id); in meson_mx_mmc_add_host()
543 return -EINVAL; in meson_mx_mmc_add_host()
547 ret = mmc_regulator_get_supply(mmc); in meson_mx_mmc_add_host()
551 mmc->max_req_size = MESON_MX_SDIO_BOUNCE_REQ_SIZE; in meson_mx_mmc_add_host()
552 mmc->max_seg_size = mmc->max_req_size; in meson_mx_mmc_add_host()
553 mmc->max_blk_count = in meson_mx_mmc_add_host()
556 mmc->max_blk_size = FIELD_GET(MESON_MX_SDIO_EXT_DATA_RW_NUMBER_MASK, in meson_mx_mmc_add_host()
558 mmc->max_blk_size -= (4 * MESON_MX_SDIO_RESPONSE_CRC16_BITS); in meson_mx_mmc_add_host()
559 mmc->max_blk_size /= BITS_PER_BYTE; in meson_mx_mmc_add_host()
562 mmc->f_min = clk_round_rate(host->cfg_div_clk, 1); in meson_mx_mmc_add_host()
563 mmc->f_max = clk_round_rate(host->cfg_div_clk, in meson_mx_mmc_add_host()
564 clk_get_rate(host->parent_clk)); in meson_mx_mmc_add_host()
566 mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; in meson_mx_mmc_add_host()
567 mmc->ops = &meson_mx_mmc_ops; in meson_mx_mmc_add_host()
569 ret = mmc_of_parse(mmc); in meson_mx_mmc_add_host()
573 ret = mmc_add_host(mmc); in meson_mx_mmc_add_host()
580 static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host) in meson_mx_mmc_register_clks() argument
585 clk_fixed_factor_parent = __clk_get_name(host->parent_clk); in meson_mx_mmc_register_clks()
586 init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, in meson_mx_mmc_register_clks()
588 dev_name(host->controller_dev)); in meson_mx_mmc_register_clks()
590 return -ENOMEM; in meson_mx_mmc_register_clks()
596 host->fixed_factor.div = 2; in meson_mx_mmc_register_clks()
597 host->fixed_factor.mult = 1; in meson_mx_mmc_register_clks()
598 host->fixed_factor.hw.init = &init; in meson_mx_mmc_register_clks()
600 host->fixed_factor_clk = devm_clk_register(host->controller_dev, in meson_mx_mmc_register_clks()
601 &host->fixed_factor.hw); in meson_mx_mmc_register_clks()
602 if (WARN_ON(IS_ERR(host->fixed_factor_clk))) in meson_mx_mmc_register_clks()
603 return PTR_ERR(host->fixed_factor_clk); in meson_mx_mmc_register_clks()
605 clk_div_parent = __clk_get_name(host->fixed_factor_clk); in meson_mx_mmc_register_clks()
606 init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, in meson_mx_mmc_register_clks()
607 "%s#div", dev_name(host->controller_dev)); in meson_mx_mmc_register_clks()
609 return -ENOMEM; in meson_mx_mmc_register_clks()
615 host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF; in meson_mx_mmc_register_clks()
616 host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT; in meson_mx_mmc_register_clks()
617 host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH; in meson_mx_mmc_register_clks()
618 host->cfg_div.hw.init = &init; in meson_mx_mmc_register_clks()
619 host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO; in meson_mx_mmc_register_clks()
621 host->cfg_div_clk = devm_clk_register(host->controller_dev, in meson_mx_mmc_register_clks()
622 &host->cfg_div.hw); in meson_mx_mmc_register_clks()
623 if (WARN_ON(IS_ERR(host->cfg_div_clk))) in meson_mx_mmc_register_clks()
624 return PTR_ERR(host->cfg_div_clk); in meson_mx_mmc_register_clks()
632 struct mmc_host *mmc; in meson_mx_mmc_probe() local
633 struct meson_mx_mmc_host *host; in meson_mx_mmc_probe() local
637 slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev); in meson_mx_mmc_probe()
639 return -ENODEV; in meson_mx_mmc_probe()
643 mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev); in meson_mx_mmc_probe()
644 if (!mmc) { in meson_mx_mmc_probe()
645 ret = -ENOMEM; in meson_mx_mmc_probe()
649 host = mmc_priv(mmc); in meson_mx_mmc_probe()
650 host->mmc = mmc; in meson_mx_mmc_probe()
651 host->controller_dev = &pdev->dev; in meson_mx_mmc_probe()
653 spin_lock_init(&host->irq_lock); in meson_mx_mmc_probe()
654 timer_setup(&host->cmd_timeout, meson_mx_mmc_timeout, 0); in meson_mx_mmc_probe()
656 platform_set_drvdata(pdev, host); in meson_mx_mmc_probe()
658 host->base = devm_platform_ioremap_resource(pdev, 0); in meson_mx_mmc_probe()
659 if (IS_ERR(host->base)) { in meson_mx_mmc_probe()
660 ret = PTR_ERR(host->base); in meson_mx_mmc_probe()
670 ret = devm_request_threaded_irq(host->controller_dev, irq, in meson_mx_mmc_probe()
673 NULL, host); in meson_mx_mmc_probe()
677 host->core_clk = devm_clk_get(host->controller_dev, "core"); in meson_mx_mmc_probe()
678 if (IS_ERR(host->core_clk)) { in meson_mx_mmc_probe()
679 ret = PTR_ERR(host->core_clk); in meson_mx_mmc_probe()
683 host->parent_clk = devm_clk_get(host->controller_dev, "clkin"); in meson_mx_mmc_probe()
684 if (IS_ERR(host->parent_clk)) { in meson_mx_mmc_probe()
685 ret = PTR_ERR(host->parent_clk); in meson_mx_mmc_probe()
689 ret = meson_mx_mmc_register_clks(host); in meson_mx_mmc_probe()
693 ret = clk_prepare_enable(host->core_clk); in meson_mx_mmc_probe()
695 dev_err(host->controller_dev, "Failed to enable core clock\n"); in meson_mx_mmc_probe()
699 ret = clk_prepare_enable(host->cfg_div_clk); in meson_mx_mmc_probe()
701 dev_err(host->controller_dev, "Failed to enable MMC clock\n"); in meson_mx_mmc_probe()
710 writel(conf, host->base + MESON_MX_SDIO_CONF); in meson_mx_mmc_probe()
712 meson_mx_mmc_soft_reset(host); in meson_mx_mmc_probe()
714 ret = meson_mx_mmc_add_host(host); in meson_mx_mmc_probe()
721 clk_disable_unprepare(host->cfg_div_clk); in meson_mx_mmc_probe()
723 clk_disable_unprepare(host->core_clk); in meson_mx_mmc_probe()
725 mmc_free_host(mmc); in meson_mx_mmc_probe()
727 of_platform_device_destroy(&slot_pdev->dev, NULL); in meson_mx_mmc_probe()
733 struct meson_mx_mmc_host *host = platform_get_drvdata(pdev); in meson_mx_mmc_remove() local
734 struct device *slot_dev = mmc_dev(host->mmc); in meson_mx_mmc_remove()
736 del_timer_sync(&host->cmd_timeout); in meson_mx_mmc_remove()
738 mmc_remove_host(host->mmc); in meson_mx_mmc_remove()
742 clk_disable_unprepare(host->cfg_div_clk); in meson_mx_mmc_remove()
743 clk_disable_unprepare(host->core_clk); in meson_mx_mmc_remove()
745 mmc_free_host(host->mmc); in meson_mx_mmc_remove()
749 { .compatible = "amlogic,meson8-sdio", },
750 { .compatible = "amlogic,meson8b-sdio", },
759 .name = "meson-mx-sdio",
767 MODULE_DESCRIPTION("Meson6, Meson8 and Meson8b SDIO/MMC Host Driver");