root/drivers/mmc/host/sdhci-of-dwcmshc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dwcmshc_adma_write_desc
  2. dwcmshc_probe
  3. dwcmshc_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller
   4  *
   5  * Copyright (C) 2018 Synaptics Incorporated
   6  *
   7  * Author: Jisheng Zhang <jszhang@kernel.org>
   8  */
   9 
  10 #include <linux/clk.h>
  11 #include <linux/dma-mapping.h>
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 #include <linux/of.h>
  15 #include <linux/sizes.h>
  16 
  17 #include "sdhci-pltfm.h"
  18 
  19 #define BOUNDARY_OK(addr, len) \
  20         ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
  21 
  22 struct dwcmshc_priv {
  23         struct clk      *bus_clk;
  24 };
  25 
  26 /*
  27  * If DMA addr spans 128MB boundary, we split the DMA transfer into two
  28  * so that each DMA transfer doesn't exceed the boundary.
  29  */
  30 static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
  31                                     dma_addr_t addr, int len, unsigned int cmd)
  32 {
  33         int tmplen, offset;
  34 
  35         if (likely(!len || BOUNDARY_OK(addr, len))) {
  36                 sdhci_adma_write_desc(host, desc, addr, len, cmd);
  37                 return;
  38         }
  39 
  40         offset = addr & (SZ_128M - 1);
  41         tmplen = SZ_128M - offset;
  42         sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
  43 
  44         addr += tmplen;
  45         len -= tmplen;
  46         sdhci_adma_write_desc(host, desc, addr, len, cmd);
  47 }
  48 
  49 static const struct sdhci_ops sdhci_dwcmshc_ops = {
  50         .set_clock              = sdhci_set_clock,
  51         .set_bus_width          = sdhci_set_bus_width,
  52         .set_uhs_signaling      = sdhci_set_uhs_signaling,
  53         .get_max_clock          = sdhci_pltfm_clk_get_max_clock,
  54         .reset                  = sdhci_reset,
  55         .adma_write_desc        = dwcmshc_adma_write_desc,
  56 };
  57 
  58 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
  59         .ops = &sdhci_dwcmshc_ops,
  60         .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
  61 };
  62 
  63 static int dwcmshc_probe(struct platform_device *pdev)
  64 {
  65         struct sdhci_pltfm_host *pltfm_host;
  66         struct sdhci_host *host;
  67         struct dwcmshc_priv *priv;
  68         int err;
  69         u32 extra;
  70 
  71         host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata,
  72                                 sizeof(struct dwcmshc_priv));
  73         if (IS_ERR(host))
  74                 return PTR_ERR(host);
  75 
  76         /*
  77          * extra adma table cnt for cross 128M boundary handling.
  78          */
  79         extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M);
  80         if (extra > SDHCI_MAX_SEGS)
  81                 extra = SDHCI_MAX_SEGS;
  82         host->adma_table_cnt += extra;
  83 
  84         pltfm_host = sdhci_priv(host);
  85         priv = sdhci_pltfm_priv(pltfm_host);
  86 
  87         pltfm_host->clk = devm_clk_get(&pdev->dev, "core");
  88         if (IS_ERR(pltfm_host->clk)) {
  89                 err = PTR_ERR(pltfm_host->clk);
  90                 dev_err(&pdev->dev, "failed to get core clk: %d\n", err);
  91                 goto free_pltfm;
  92         }
  93         err = clk_prepare_enable(pltfm_host->clk);
  94         if (err)
  95                 goto free_pltfm;
  96 
  97         priv->bus_clk = devm_clk_get(&pdev->dev, "bus");
  98         if (!IS_ERR(priv->bus_clk))
  99                 clk_prepare_enable(priv->bus_clk);
 100 
 101         err = mmc_of_parse(host->mmc);
 102         if (err)
 103                 goto err_clk;
 104 
 105         sdhci_get_of_property(pdev);
 106 
 107         err = sdhci_add_host(host);
 108         if (err)
 109                 goto err_clk;
 110 
 111         return 0;
 112 
 113 err_clk:
 114         clk_disable_unprepare(pltfm_host->clk);
 115         clk_disable_unprepare(priv->bus_clk);
 116 free_pltfm:
 117         sdhci_pltfm_free(pdev);
 118         return err;
 119 }
 120 
 121 static int dwcmshc_remove(struct platform_device *pdev)
 122 {
 123         struct sdhci_host *host = platform_get_drvdata(pdev);
 124         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 125         struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
 126 
 127         sdhci_remove_host(host, 0);
 128 
 129         clk_disable_unprepare(pltfm_host->clk);
 130         clk_disable_unprepare(priv->bus_clk);
 131 
 132         sdhci_pltfm_free(pdev);
 133 
 134         return 0;
 135 }
 136 
 137 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
 138         { .compatible = "snps,dwcmshc-sdhci" },
 139         {}
 140 };
 141 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
 142 
 143 static struct platform_driver sdhci_dwcmshc_driver = {
 144         .driver = {
 145                 .name   = "sdhci-dwcmshc",
 146                 .of_match_table = sdhci_dwcmshc_dt_ids,
 147         },
 148         .probe  = dwcmshc_probe,
 149         .remove = dwcmshc_remove,
 150 };
 151 module_platform_driver(sdhci_dwcmshc_driver);
 152 
 153 MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC");
 154 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
 155 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */