linux/drivers/mmc/host/renesas_sdhi_internal_dmac.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * DMA support for Internal DMAC with SDHI SD/SDIO controller
   4 *
   5 * Copyright (C) 2016-19 Renesas Electronics Corporation
   6 * Copyright (C) 2016-17 Horms Solutions, Simon Horman
   7 * Copyright (C) 2018-19 Sang Engineering, Wolfram Sang
   8 */
   9
  10#include <linux/bitops.h>
  11#include <linux/device.h>
  12#include <linux/dma-mapping.h>
  13#include <linux/io-64-nonatomic-hi-lo.h>
  14#include <linux/mfd/tmio.h>
  15#include <linux/mmc/host.h>
  16#include <linux/mod_devicetable.h>
  17#include <linux/module.h>
  18#include <linux/pagemap.h>
  19#include <linux/scatterlist.h>
  20#include <linux/sys_soc.h>
  21
  22#include "renesas_sdhi.h"
  23#include "tmio_mmc.h"
  24
  25#define DM_CM_DTRAN_MODE        0x820
  26#define DM_CM_DTRAN_CTRL        0x828
  27#define DM_CM_RST               0x830
  28#define DM_CM_INFO1             0x840
  29#define DM_CM_INFO1_MASK        0x848
  30#define DM_CM_INFO2             0x850
  31#define DM_CM_INFO2_MASK        0x858
  32#define DM_DTRAN_ADDR           0x880
  33
  34/* DM_CM_DTRAN_MODE */
  35#define DTRAN_MODE_CH_NUM_CH0   0       /* "downstream" = for write commands */
  36#define DTRAN_MODE_CH_NUM_CH1   BIT(16) /* "upstream" = for read commands */
  37#define DTRAN_MODE_BUS_WIDTH    (BIT(5) | BIT(4))
  38#define DTRAN_MODE_ADDR_MODE    BIT(0)  /* 1 = Increment address, 0 = Fixed */
  39
  40/* DM_CM_DTRAN_CTRL */
  41#define DTRAN_CTRL_DM_START     BIT(0)
  42
  43/* DM_CM_RST */
  44#define RST_DTRANRST1           BIT(9)
  45#define RST_DTRANRST0           BIT(8)
  46#define RST_RESERVED_BITS       GENMASK_ULL(31, 0)
  47
  48/* DM_CM_INFO1 and DM_CM_INFO1_MASK */
  49#define INFO1_CLEAR             0
  50#define INFO1_MASK_CLEAR        GENMASK_ULL(31, 0)
  51#define INFO1_DTRANEND1         BIT(17)
  52#define INFO1_DTRANEND0         BIT(16)
  53
  54/* DM_CM_INFO2 and DM_CM_INFO2_MASK */
  55#define INFO2_MASK_CLEAR        GENMASK_ULL(31, 0)
  56#define INFO2_DTRANERR1         BIT(17)
  57#define INFO2_DTRANERR0         BIT(16)
  58
  59/*
  60 * Specification of this driver:
  61 * - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
  62 * - Since this SDHI DMAC register set has 16 but 32-bit width, we
  63 *   need a custom accessor.
  64 */
  65
  66static unsigned long global_flags;
  67/*
  68 * Workaround for avoiding to use RX DMAC by multiple channels.
  69 * On R-Car H3 ES1.* and M3-W ES1.0, when multiple SDHI channels use
  70 * RX DMAC simultaneously, sometimes hundreds of bytes data are not
  71 * stored into the system memory even if the DMAC interrupt happened.
  72 * So, this driver then uses one RX DMAC channel only.
  73 */
  74#define SDHI_INTERNAL_DMAC_ONE_RX_ONLY  0
  75#define SDHI_INTERNAL_DMAC_RX_IN_USE    1
  76
  77/* RZ/A2 does not have the ADRR_MODE bit */
  78#define SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY 2
  79
  80/* Definitions for sampling clocks */
  81static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
  82        {
  83                .clk_rate = 0,
  84                .tap = 0x00000300,
  85                .tap_hs400_4tap = 0x00000100,
  86        },
  87};
  88
  89static const struct renesas_sdhi_of_data of_rza2_compatible = {
  90        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
  91                          TMIO_MMC_HAVE_CBSY,
  92        .tmio_ocr_mask  = MMC_VDD_32_33,
  93        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
  94                          MMC_CAP_CMD23,
  95        .bus_shift      = 2,
  96        .scc_offset     = 0 - 0x1000,
  97        .taps           = rcar_gen3_scc_taps,
  98        .taps_num       = ARRAY_SIZE(rcar_gen3_scc_taps),
  99        /* DMAC can handle 32bit blk count but only 1 segment */
 100        .max_blk_count  = UINT_MAX / TMIO_MAX_BLK_SIZE,
 101        .max_segs       = 1,
 102};
 103
 104static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
 105        .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
 106                          TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
 107        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
 108                          MMC_CAP_CMD23,
 109        .capabilities2  = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
 110        .bus_shift      = 2,
 111        .scc_offset     = 0x1000,
 112        .taps           = rcar_gen3_scc_taps,
 113        .taps_num       = ARRAY_SIZE(rcar_gen3_scc_taps),
 114        /* DMAC can handle 32bit blk count but only 1 segment */
 115        .max_blk_count  = UINT_MAX / TMIO_MAX_BLK_SIZE,
 116        .max_segs       = 1,
 117};
 118
 119static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
 120        { .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, },
 121        { .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, },
 122        { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
 123        { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
 124        { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
 125        {},
 126};
 127MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);
 128
 129static void
 130renesas_sdhi_internal_dmac_dm_write(struct tmio_mmc_host *host,
 131                                    int addr, u64 val)
 132{
 133        writeq(val, host->ctl + addr);
 134}
 135
 136static void
 137renesas_sdhi_internal_dmac_enable_dma(struct tmio_mmc_host *host, bool enable)
 138{
 139        struct renesas_sdhi *priv = host_to_priv(host);
 140
 141        if (!host->chan_tx || !host->chan_rx)
 142                return;
 143
 144        if (!enable)
 145                renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1,
 146                                                    INFO1_CLEAR);
 147
 148        if (priv->dma_priv.enable)
 149                priv->dma_priv.enable(host, enable);
 150}
 151
 152static void
 153renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host) {
 154        u64 val = RST_DTRANRST1 | RST_DTRANRST0;
 155
 156        renesas_sdhi_internal_dmac_enable_dma(host, false);
 157
 158        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
 159                                            RST_RESERVED_BITS & ~val);
 160        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
 161                                            RST_RESERVED_BITS | val);
 162
 163        clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
 164
 165        renesas_sdhi_internal_dmac_enable_dma(host, true);
 166}
 167
 168static void
 169renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) {
 170        struct renesas_sdhi *priv = host_to_priv(host);
 171
 172        tasklet_schedule(&priv->dma_priv.dma_complete);
 173}
 174
 175static void
 176renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
 177                                     struct mmc_data *data)
 178{
 179        struct scatterlist *sg = host->sg_ptr;
 180        u32 dtran_mode = DTRAN_MODE_BUS_WIDTH;
 181
 182        if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags))
 183                dtran_mode |= DTRAN_MODE_ADDR_MODE;
 184
 185        if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
 186                        mmc_get_dma_dir(data)))
 187                goto force_pio;
 188
 189        /* This DMAC cannot handle if buffer is not 8-bytes alignment */
 190        if (!IS_ALIGNED(sg_dma_address(sg), 8))
 191                goto force_pio_with_unmap;
 192
 193        if (data->flags & MMC_DATA_READ) {
 194                dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
 195                if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
 196                    test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
 197                        goto force_pio_with_unmap;
 198        } else {
 199                dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
 200        }
 201
 202        renesas_sdhi_internal_dmac_enable_dma(host, true);
 203
 204        /* set dma parameters */
 205        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
 206                                            dtran_mode);
 207        renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
 208                                            sg_dma_address(sg));
 209
 210        host->dma_on = true;
 211
 212        return;
 213
 214force_pio_with_unmap:
 215        dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data));
 216
 217force_pio:
 218        renesas_sdhi_internal_dmac_enable_dma(host, false);
 219}
 220
 221static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
 222{
 223        struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
 224
 225        tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
 226
 227        /* start the DMAC */
 228        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_CTRL,
 229                                            DTRAN_CTRL_DM_START);
 230}
 231
 232static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
 233{
 234        enum dma_data_direction dir;
 235
 236        if (!host->dma_on)
 237                return false;
 238
 239        if (!host->data)
 240                return false;
 241
 242        if (host->data->flags & MMC_DATA_READ)
 243                dir = DMA_FROM_DEVICE;
 244        else
 245                dir = DMA_TO_DEVICE;
 246
 247        renesas_sdhi_internal_dmac_enable_dma(host, false);
 248        dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
 249
 250        if (dir == DMA_FROM_DEVICE)
 251                clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
 252
 253        host->dma_on = false;
 254
 255        return true;
 256}
 257
 258static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
 259{
 260        struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
 261
 262        spin_lock_irq(&host->lock);
 263        if (!renesas_sdhi_internal_dmac_complete(host))
 264                goto out;
 265
 266        tmio_mmc_do_data_irq(host);
 267out:
 268        spin_unlock_irq(&host->lock);
 269}
 270
 271static void renesas_sdhi_internal_dmac_end_dma(struct tmio_mmc_host *host)
 272{
 273        if (host->data)
 274                renesas_sdhi_internal_dmac_complete(host);
 275}
 276
 277static void
 278renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
 279                                       struct tmio_mmc_data *pdata)
 280{
 281        struct renesas_sdhi *priv = host_to_priv(host);
 282
 283        /* Disable DMAC interrupts, we don't use them */
 284        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1_MASK,
 285                                            INFO1_MASK_CLEAR);
 286        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO2_MASK,
 287                                            INFO2_MASK_CLEAR);
 288
 289        /* Each value is set to non-zero to assume "enabling" each DMA */
 290        host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
 291
 292        tasklet_init(&priv->dma_priv.dma_complete,
 293                     renesas_sdhi_internal_dmac_complete_tasklet_fn,
 294                     (unsigned long)host);
 295        tasklet_init(&host->dma_issue,
 296                     renesas_sdhi_internal_dmac_issue_tasklet_fn,
 297                     (unsigned long)host);
 298}
 299
 300static void
 301renesas_sdhi_internal_dmac_release_dma(struct tmio_mmc_host *host)
 302{
 303        /* Each value is set to zero to assume "disabling" each DMA */
 304        host->chan_rx = host->chan_tx = NULL;
 305}
 306
 307static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
 308        .start = renesas_sdhi_internal_dmac_start_dma,
 309        .enable = renesas_sdhi_internal_dmac_enable_dma,
 310        .request = renesas_sdhi_internal_dmac_request_dma,
 311        .release = renesas_sdhi_internal_dmac_release_dma,
 312        .abort = renesas_sdhi_internal_dmac_abort_dma,
 313        .dataend = renesas_sdhi_internal_dmac_dataend_dma,
 314        .end = renesas_sdhi_internal_dmac_end_dma,
 315};
 316
 317/*
 318 * Whitelist of specific R-Car Gen3 SoC ES versions to use this DMAC
 319 * implementation as others may use a different implementation.
 320 */
 321static const struct soc_device_attribute soc_dma_quirks[] = {
 322        { .soc_id = "r7s9210",
 323          .data = (void *)BIT(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY) },
 324        { .soc_id = "r8a7795", .revision = "ES1.*",
 325          .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
 326        { .soc_id = "r8a7796", .revision = "ES1.0",
 327          .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
 328        { /* sentinel */ }
 329};
 330
 331static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
 332{
 333        const struct soc_device_attribute *soc = soc_device_match(soc_dma_quirks);
 334        struct device *dev = &pdev->dev;
 335
 336        if (soc)
 337                global_flags |= (unsigned long)soc->data;
 338
 339        dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
 340        if (!dev->dma_parms)
 341                return -ENOMEM;
 342
 343        /* value is max of SD_SECCNT. Confirmed by HW engineers */
 344        dma_set_max_seg_size(dev, 0xffffffff);
 345
 346        return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
 347}
 348
 349static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
 350        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 351                                pm_runtime_force_resume)
 352        SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
 353                           tmio_mmc_host_runtime_resume,
 354                           NULL)
 355};
 356
 357static struct platform_driver renesas_internal_dmac_sdhi_driver = {
 358        .driver         = {
 359                .name   = "renesas_sdhi_internal_dmac",
 360                .pm     = &renesas_sdhi_internal_dmac_dev_pm_ops,
 361                .of_match_table = renesas_sdhi_internal_dmac_of_match,
 362        },
 363        .probe          = renesas_sdhi_internal_dmac_probe,
 364        .remove         = renesas_sdhi_remove,
 365};
 366
 367module_platform_driver(renesas_internal_dmac_sdhi_driver);
 368
 369MODULE_DESCRIPTION("Renesas SDHI driver for internal DMAC");
 370MODULE_AUTHOR("Yoshihiro Shimoda");
 371MODULE_LICENSE("GPL v2");
 372