0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/device.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/dmaengine.h>
0014 #include <linux/mfd/tmio.h>
0015 #include <linux/mmc/host.h>
0016 #include <linux/mod_devicetable.h>
0017 #include <linux/module.h>
0018 #include <linux/of_device.h>
0019 #include <linux/pagemap.h>
0020 #include <linux/scatterlist.h>
0021 #include <linux/sys_soc.h>
0022
0023 #include "renesas_sdhi.h"
0024 #include "tmio_mmc.h"
0025
0026 #define TMIO_MMC_MIN_DMA_LEN 8
0027
0028 static const struct renesas_sdhi_of_data of_default_cfg = {
0029 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
0030 };
0031
0032 static const struct renesas_sdhi_of_data of_rz_compatible = {
0033 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_32BIT_DATA_PORT |
0034 TMIO_MMC_HAVE_CBSY,
0035 .tmio_ocr_mask = MMC_VDD_32_33,
0036 .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
0037 MMC_CAP_WAIT_WHILE_BUSY,
0038 };
0039
0040 static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
0041 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
0042 .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
0043 MMC_CAP_WAIT_WHILE_BUSY,
0044 .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
0045 };
0046
0047
0048 static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
0049 {
0050 .clk_rate = 156000000,
0051 .tap = 0x00000703,
0052 },
0053 {
0054 .clk_rate = 0,
0055 .tap = 0x00000300,
0056 },
0057 };
0058
0059 static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
0060 .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
0061 TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
0062 .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
0063 MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
0064 .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
0065 .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
0066 .dma_rx_offset = 0x2000,
0067 .scc_offset = 0x0300,
0068 .taps = rcar_gen2_scc_taps,
0069 .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps),
0070 .max_blk_count = UINT_MAX / TMIO_MAX_BLK_SIZE,
0071 };
0072
0073 static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = {
0074 { .compatible = "renesas,sdhi-sh73a0", .data = &of_default_cfg, },
0075 { .compatible = "renesas,sdhi-r8a73a4", .data = &of_default_cfg, },
0076 { .compatible = "renesas,sdhi-r8a7740", .data = &of_default_cfg, },
0077 { .compatible = "renesas,sdhi-r7s72100", .data = &of_rz_compatible, },
0078 { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, },
0079 { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
0080 { .compatible = "renesas,sdhi-r8a7743", .data = &of_rcar_gen2_compatible, },
0081 { .compatible = "renesas,sdhi-r8a7745", .data = &of_rcar_gen2_compatible, },
0082 { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
0083 { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
0084 { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, },
0085 { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
0086 { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
0087 { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, },
0088 { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, },
0089 { .compatible = "renesas,sdhi-shmobile" },
0090 {},
0091 };
0092 MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match);
0093
0094 static void renesas_sdhi_sys_dmac_enable_dma(struct tmio_mmc_host *host,
0095 bool enable)
0096 {
0097 struct renesas_sdhi *priv = host_to_priv(host);
0098
0099 if (!host->chan_tx || !host->chan_rx)
0100 return;
0101
0102 if (priv->dma_priv.enable)
0103 priv->dma_priv.enable(host, enable);
0104 }
0105
0106 static void renesas_sdhi_sys_dmac_abort_dma(struct tmio_mmc_host *host)
0107 {
0108 renesas_sdhi_sys_dmac_enable_dma(host, false);
0109
0110 if (host->chan_rx)
0111 dmaengine_terminate_sync(host->chan_rx);
0112 if (host->chan_tx)
0113 dmaengine_terminate_sync(host->chan_tx);
0114
0115 renesas_sdhi_sys_dmac_enable_dma(host, true);
0116 }
0117
0118 static void renesas_sdhi_sys_dmac_dataend_dma(struct tmio_mmc_host *host)
0119 {
0120 struct renesas_sdhi *priv = host_to_priv(host);
0121
0122 complete(&priv->dma_priv.dma_dataend);
0123 }
0124
0125 static void renesas_sdhi_sys_dmac_dma_callback(void *arg)
0126 {
0127 struct tmio_mmc_host *host = arg;
0128 struct renesas_sdhi *priv = host_to_priv(host);
0129
0130 spin_lock_irq(&host->lock);
0131
0132 if (!host->data)
0133 goto out;
0134
0135 if (host->data->flags & MMC_DATA_READ)
0136 dma_unmap_sg(host->chan_rx->device->dev,
0137 host->sg_ptr, host->sg_len,
0138 DMA_FROM_DEVICE);
0139 else
0140 dma_unmap_sg(host->chan_tx->device->dev,
0141 host->sg_ptr, host->sg_len,
0142 DMA_TO_DEVICE);
0143
0144 spin_unlock_irq(&host->lock);
0145
0146 wait_for_completion(&priv->dma_priv.dma_dataend);
0147
0148 spin_lock_irq(&host->lock);
0149 tmio_mmc_do_data_irq(host);
0150 out:
0151 spin_unlock_irq(&host->lock);
0152 }
0153
0154 static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
0155 {
0156 struct renesas_sdhi *priv = host_to_priv(host);
0157 struct scatterlist *sg = host->sg_ptr, *sg_tmp;
0158 struct dma_async_tx_descriptor *desc = NULL;
0159 struct dma_chan *chan = host->chan_rx;
0160 dma_cookie_t cookie;
0161 int ret, i;
0162 bool aligned = true, multiple = true;
0163 unsigned int align = (1 << host->pdata->alignment_shift) - 1;
0164
0165 for_each_sg(sg, sg_tmp, host->sg_len, i) {
0166 if (sg_tmp->offset & align)
0167 aligned = false;
0168 if (sg_tmp->length & align) {
0169 multiple = false;
0170 break;
0171 }
0172 }
0173
0174 if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE ||
0175 (align & PAGE_MASK))) || !multiple) {
0176 ret = -EINVAL;
0177 goto pio;
0178 }
0179
0180 if (sg->length < TMIO_MMC_MIN_DMA_LEN)
0181 return;
0182
0183
0184 if (!aligned) {
0185 sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
0186 host->sg_ptr = &host->bounce_sg;
0187 sg = host->sg_ptr;
0188 }
0189
0190 ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE);
0191 if (ret > 0)
0192 desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_DEV_TO_MEM,
0193 DMA_CTRL_ACK);
0194
0195 if (desc) {
0196 reinit_completion(&priv->dma_priv.dma_dataend);
0197 desc->callback = renesas_sdhi_sys_dmac_dma_callback;
0198 desc->callback_param = host;
0199
0200 cookie = dmaengine_submit(desc);
0201 if (cookie < 0) {
0202 desc = NULL;
0203 ret = cookie;
0204 }
0205 host->dma_on = true;
0206 }
0207 pio:
0208 if (!desc) {
0209
0210 renesas_sdhi_sys_dmac_enable_dma(host, false);
0211 if (ret >= 0)
0212 ret = -EIO;
0213 host->chan_rx = NULL;
0214 dma_release_channel(chan);
0215
0216 chan = host->chan_tx;
0217 if (chan) {
0218 host->chan_tx = NULL;
0219 dma_release_channel(chan);
0220 }
0221 dev_warn(&host->pdev->dev,
0222 "DMA failed: %d, falling back to PIO\n", ret);
0223 }
0224 }
0225
0226 static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
0227 {
0228 struct renesas_sdhi *priv = host_to_priv(host);
0229 struct scatterlist *sg = host->sg_ptr, *sg_tmp;
0230 struct dma_async_tx_descriptor *desc = NULL;
0231 struct dma_chan *chan = host->chan_tx;
0232 dma_cookie_t cookie;
0233 int ret, i;
0234 bool aligned = true, multiple = true;
0235 unsigned int align = (1 << host->pdata->alignment_shift) - 1;
0236
0237 for_each_sg(sg, sg_tmp, host->sg_len, i) {
0238 if (sg_tmp->offset & align)
0239 aligned = false;
0240 if (sg_tmp->length & align) {
0241 multiple = false;
0242 break;
0243 }
0244 }
0245
0246 if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_SIZE ||
0247 (align & PAGE_MASK))) || !multiple) {
0248 ret = -EINVAL;
0249 goto pio;
0250 }
0251
0252 if (sg->length < TMIO_MMC_MIN_DMA_LEN)
0253 return;
0254
0255
0256 if (!aligned) {
0257 unsigned long flags;
0258 void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags);
0259
0260 sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
0261 memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length);
0262 tmio_mmc_kunmap_atomic(sg, &flags, sg_vaddr);
0263 host->sg_ptr = &host->bounce_sg;
0264 sg = host->sg_ptr;
0265 }
0266
0267 ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE);
0268 if (ret > 0)
0269 desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV,
0270 DMA_CTRL_ACK);
0271
0272 if (desc) {
0273 reinit_completion(&priv->dma_priv.dma_dataend);
0274 desc->callback = renesas_sdhi_sys_dmac_dma_callback;
0275 desc->callback_param = host;
0276
0277 cookie = dmaengine_submit(desc);
0278 if (cookie < 0) {
0279 desc = NULL;
0280 ret = cookie;
0281 }
0282 host->dma_on = true;
0283 }
0284 pio:
0285 if (!desc) {
0286
0287 renesas_sdhi_sys_dmac_enable_dma(host, false);
0288 if (ret >= 0)
0289 ret = -EIO;
0290 host->chan_tx = NULL;
0291 dma_release_channel(chan);
0292
0293 chan = host->chan_rx;
0294 if (chan) {
0295 host->chan_rx = NULL;
0296 dma_release_channel(chan);
0297 }
0298 dev_warn(&host->pdev->dev,
0299 "DMA failed: %d, falling back to PIO\n", ret);
0300 }
0301 }
0302
0303 static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host,
0304 struct mmc_data *data)
0305 {
0306 if (data->flags & MMC_DATA_READ) {
0307 if (host->chan_rx)
0308 renesas_sdhi_sys_dmac_start_dma_rx(host);
0309 } else {
0310 if (host->chan_tx)
0311 renesas_sdhi_sys_dmac_start_dma_tx(host);
0312 }
0313 }
0314
0315 static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv)
0316 {
0317 struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv;
0318 struct dma_chan *chan = NULL;
0319
0320 spin_lock_irq(&host->lock);
0321
0322 if (host->data) {
0323 if (host->data->flags & MMC_DATA_READ)
0324 chan = host->chan_rx;
0325 else
0326 chan = host->chan_tx;
0327 }
0328
0329 spin_unlock_irq(&host->lock);
0330
0331 tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
0332
0333 if (chan)
0334 dma_async_issue_pending(chan);
0335 }
0336
0337 static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host,
0338 struct tmio_mmc_data *pdata)
0339 {
0340 struct renesas_sdhi *priv = host_to_priv(host);
0341
0342
0343 if (!host->pdev->dev.of_node &&
0344 (!pdata->chan_priv_tx || !pdata->chan_priv_rx))
0345 return;
0346
0347 if (!host->chan_tx && !host->chan_rx) {
0348 struct resource *res = platform_get_resource(host->pdev,
0349 IORESOURCE_MEM, 0);
0350 struct dma_slave_config cfg = {};
0351 dma_cap_mask_t mask;
0352 int ret;
0353
0354 if (!res)
0355 return;
0356
0357 dma_cap_zero(mask);
0358 dma_cap_set(DMA_SLAVE, mask);
0359
0360 host->chan_tx = dma_request_slave_channel_compat(mask,
0361 priv->dma_priv.filter, pdata->chan_priv_tx,
0362 &host->pdev->dev, "tx");
0363 dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
0364 host->chan_tx);
0365
0366 if (!host->chan_tx)
0367 return;
0368
0369 cfg.direction = DMA_MEM_TO_DEV;
0370 cfg.dst_addr = res->start +
0371 (CTL_SD_DATA_PORT << host->bus_shift);
0372 cfg.dst_addr_width = priv->dma_priv.dma_buswidth;
0373 if (!cfg.dst_addr_width)
0374 cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
0375 cfg.src_addr = 0;
0376 ret = dmaengine_slave_config(host->chan_tx, &cfg);
0377 if (ret < 0)
0378 goto ecfgtx;
0379
0380 host->chan_rx = dma_request_slave_channel_compat(mask,
0381 priv->dma_priv.filter, pdata->chan_priv_rx,
0382 &host->pdev->dev, "rx");
0383 dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
0384 host->chan_rx);
0385
0386 if (!host->chan_rx)
0387 goto ereqrx;
0388
0389 cfg.direction = DMA_DEV_TO_MEM;
0390 cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset;
0391 cfg.src_addr_width = priv->dma_priv.dma_buswidth;
0392 if (!cfg.src_addr_width)
0393 cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
0394 cfg.dst_addr = 0;
0395 ret = dmaengine_slave_config(host->chan_rx, &cfg);
0396 if (ret < 0)
0397 goto ecfgrx;
0398
0399 host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA);
0400 if (!host->bounce_buf)
0401 goto ebouncebuf;
0402
0403 init_completion(&priv->dma_priv.dma_dataend);
0404 tasklet_init(&host->dma_issue,
0405 renesas_sdhi_sys_dmac_issue_tasklet_fn,
0406 (unsigned long)host);
0407 }
0408
0409 renesas_sdhi_sys_dmac_enable_dma(host, true);
0410
0411 return;
0412
0413 ebouncebuf:
0414 ecfgrx:
0415 dma_release_channel(host->chan_rx);
0416 host->chan_rx = NULL;
0417 ereqrx:
0418 ecfgtx:
0419 dma_release_channel(host->chan_tx);
0420 host->chan_tx = NULL;
0421 }
0422
0423 static void renesas_sdhi_sys_dmac_release_dma(struct tmio_mmc_host *host)
0424 {
0425 if (host->chan_tx) {
0426 struct dma_chan *chan = host->chan_tx;
0427
0428 host->chan_tx = NULL;
0429 dma_release_channel(chan);
0430 }
0431 if (host->chan_rx) {
0432 struct dma_chan *chan = host->chan_rx;
0433
0434 host->chan_rx = NULL;
0435 dma_release_channel(chan);
0436 }
0437 if (host->bounce_buf) {
0438 free_pages((unsigned long)host->bounce_buf, 0);
0439 host->bounce_buf = NULL;
0440 }
0441 }
0442
0443 static const struct tmio_mmc_dma_ops renesas_sdhi_sys_dmac_dma_ops = {
0444 .start = renesas_sdhi_sys_dmac_start_dma,
0445 .enable = renesas_sdhi_sys_dmac_enable_dma,
0446 .request = renesas_sdhi_sys_dmac_request_dma,
0447 .release = renesas_sdhi_sys_dmac_release_dma,
0448 .abort = renesas_sdhi_sys_dmac_abort_dma,
0449 .dataend = renesas_sdhi_sys_dmac_dataend_dma,
0450 };
0451
0452 static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev)
0453 {
0454 return renesas_sdhi_probe(pdev, &renesas_sdhi_sys_dmac_dma_ops,
0455 of_device_get_match_data(&pdev->dev), NULL);
0456 }
0457
0458 static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = {
0459 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
0460 pm_runtime_force_resume)
0461 SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
0462 tmio_mmc_host_runtime_resume,
0463 NULL)
0464 };
0465
0466 static struct platform_driver renesas_sys_dmac_sdhi_driver = {
0467 .driver = {
0468 .name = "sh_mobile_sdhi",
0469 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0470 .pm = &renesas_sdhi_sys_dmac_dev_pm_ops,
0471 .of_match_table = renesas_sdhi_sys_dmac_of_match,
0472 },
0473 .probe = renesas_sdhi_sys_dmac_probe,
0474 .remove = renesas_sdhi_remove,
0475 };
0476
0477 module_platform_driver(renesas_sys_dmac_sdhi_driver);
0478
0479 MODULE_DESCRIPTION("Renesas SDHI driver");
0480 MODULE_AUTHOR("Magnus Damm");
0481 MODULE_LICENSE("GPL v2");
0482 MODULE_ALIAS("platform:sh_mobile_sdhi");