linux/drivers/mmc/host/sdhci-tegra.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2010 Google, Inc.
   3 *
   4 * This software is licensed under the terms of the GNU General Public
   5 * License version 2, as published by the Free Software Foundation, and
   6 * may be copied, distributed, and modified under those terms.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 */
  14
  15#include <linux/err.h>
  16#include <linux/module.h>
  17#include <linux/init.h>
  18#include <linux/platform_device.h>
  19#include <linux/clk.h>
  20#include <linux/io.h>
  21#include <linux/of.h>
  22#include <linux/of_device.h>
  23#include <linux/of_gpio.h>
  24#include <linux/gpio.h>
  25#include <linux/mmc/card.h>
  26#include <linux/mmc/host.h>
  27#include <linux/mmc/slot-gpio.h>
  28
  29#include <asm/gpio.h>
  30
  31#include "sdhci-pltfm.h"
  32
  33/* Tegra SDHOST controller vendor register definitions */
  34#define SDHCI_TEGRA_VENDOR_MISC_CTRL            0x120
  35#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300   0x20
  36
  37#define NVQUIRK_FORCE_SDHCI_SPEC_200    BIT(0)
  38#define NVQUIRK_ENABLE_BLOCK_GAP_DET    BIT(1)
  39#define NVQUIRK_ENABLE_SDHCI_SPEC_300   BIT(2)
  40
  41struct sdhci_tegra_soc_data {
  42        const struct sdhci_pltfm_data *pdata;
  43        u32 nvquirks;
  44};
  45
  46struct sdhci_tegra {
  47        const struct sdhci_tegra_soc_data *soc_data;
  48        int power_gpio;
  49};
  50
  51static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
  52{
  53        u32 val;
  54
  55        if (unlikely(reg == SDHCI_PRESENT_STATE)) {
  56                /* Use wp_gpio here instead? */
  57                val = readl(host->ioaddr + reg);
  58                return val | SDHCI_WRITE_PROTECT;
  59        }
  60
  61        return readl(host->ioaddr + reg);
  62}
  63
  64static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
  65{
  66        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
  67        struct sdhci_tegra *tegra_host = pltfm_host->priv;
  68        const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
  69
  70        if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
  71                        (reg == SDHCI_HOST_VERSION))) {
  72                /* Erratum: Version register is invalid in HW. */
  73                return SDHCI_SPEC_200;
  74        }
  75
  76        return readw(host->ioaddr + reg);
  77}
  78
  79static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
  80{
  81        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
  82        struct sdhci_tegra *tegra_host = pltfm_host->priv;
  83        const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
  84
  85        /* Seems like we're getting spurious timeout and crc errors, so
  86         * disable signalling of them. In case of real errors software
  87         * timers should take care of eventually detecting them.
  88         */
  89        if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
  90                val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
  91
  92        writel(val, host->ioaddr + reg);
  93
  94        if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
  95                        (reg == SDHCI_INT_ENABLE))) {
  96                /* Erratum: Must enable block gap interrupt detection */
  97                u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
  98                if (val & SDHCI_INT_CARD_INT)
  99                        gap_ctrl |= 0x8;
 100                else
 101                        gap_ctrl &= ~0x8;
 102                writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
 103        }
 104}
 105
 106static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
 107{
 108        return mmc_gpio_get_ro(host->mmc);
 109}
 110
 111static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
 112{
 113        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 114        struct sdhci_tegra *tegra_host = pltfm_host->priv;
 115        const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 116
 117        if (!(mask & SDHCI_RESET_ALL))
 118                return;
 119
 120        /* Erratum: Enable SDHCI spec v3.00 support */
 121        if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) {
 122                u32 misc_ctrl;
 123
 124                misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
 125                misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
 126                sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
 127        }
 128}
 129
 130static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
 131{
 132        u32 ctrl;
 133
 134        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 135        if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
 136            (bus_width == MMC_BUS_WIDTH_8)) {
 137                ctrl &= ~SDHCI_CTRL_4BITBUS;
 138                ctrl |= SDHCI_CTRL_8BITBUS;
 139        } else {
 140                ctrl &= ~SDHCI_CTRL_8BITBUS;
 141                if (bus_width == MMC_BUS_WIDTH_4)
 142                        ctrl |= SDHCI_CTRL_4BITBUS;
 143                else
 144                        ctrl &= ~SDHCI_CTRL_4BITBUS;
 145        }
 146        sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 147        return 0;
 148}
 149
 150static const struct sdhci_ops tegra_sdhci_ops = {
 151        .get_ro     = tegra_sdhci_get_ro,
 152        .read_l     = tegra_sdhci_readl,
 153        .read_w     = tegra_sdhci_readw,
 154        .write_l    = tegra_sdhci_writel,
 155        .platform_bus_width = tegra_sdhci_buswidth,
 156        .platform_reset_exit = tegra_sdhci_reset_exit,
 157};
 158
 159static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
 160        .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
 161                  SDHCI_QUIRK_SINGLE_POWER_WRITE |
 162                  SDHCI_QUIRK_NO_HISPD_BIT |
 163                  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
 164        .ops  = &tegra_sdhci_ops,
 165};
 166
 167static struct sdhci_tegra_soc_data soc_data_tegra20 = {
 168        .pdata = &sdhci_tegra20_pdata,
 169        .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
 170                    NVQUIRK_ENABLE_BLOCK_GAP_DET,
 171};
 172
 173static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
 174        .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
 175                  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
 176                  SDHCI_QUIRK_SINGLE_POWER_WRITE |
 177                  SDHCI_QUIRK_NO_HISPD_BIT |
 178                  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
 179        .ops  = &tegra_sdhci_ops,
 180};
 181
 182static struct sdhci_tegra_soc_data soc_data_tegra30 = {
 183        .pdata = &sdhci_tegra30_pdata,
 184        .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
 185};
 186
 187static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
 188        .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
 189                  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
 190                  SDHCI_QUIRK_SINGLE_POWER_WRITE |
 191                  SDHCI_QUIRK_NO_HISPD_BIT |
 192                  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
 193        .ops  = &tegra_sdhci_ops,
 194};
 195
 196static struct sdhci_tegra_soc_data soc_data_tegra114 = {
 197        .pdata = &sdhci_tegra114_pdata,
 198};
 199
 200static const struct of_device_id sdhci_tegra_dt_match[] = {
 201        { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
 202        { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
 203        { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
 204        {}
 205};
 206MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
 207
 208static int sdhci_tegra_parse_dt(struct device *dev)
 209{
 210        struct device_node *np = dev->of_node;
 211        struct sdhci_host *host = dev_get_drvdata(dev);
 212        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 213        struct sdhci_tegra *tegra_host = pltfm_host->priv;
 214
 215        tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
 216        return mmc_of_parse(host->mmc);
 217}
 218
 219static int sdhci_tegra_probe(struct platform_device *pdev)
 220{
 221        const struct of_device_id *match;
 222        const struct sdhci_tegra_soc_data *soc_data;
 223        struct sdhci_host *host;
 224        struct sdhci_pltfm_host *pltfm_host;
 225        struct sdhci_tegra *tegra_host;
 226        struct clk *clk;
 227        int rc;
 228
 229        match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
 230        if (!match)
 231                return -EINVAL;
 232        soc_data = match->data;
 233
 234        host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
 235        if (IS_ERR(host))
 236                return PTR_ERR(host);
 237        pltfm_host = sdhci_priv(host);
 238
 239        tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
 240        if (!tegra_host) {
 241                dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
 242                rc = -ENOMEM;
 243                goto err_alloc_tegra_host;
 244        }
 245        tegra_host->soc_data = soc_data;
 246        pltfm_host->priv = tegra_host;
 247
 248        rc = sdhci_tegra_parse_dt(&pdev->dev);
 249        if (rc)
 250                goto err_parse_dt;
 251
 252        if (gpio_is_valid(tegra_host->power_gpio)) {
 253                rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
 254                if (rc) {
 255                        dev_err(mmc_dev(host->mmc),
 256                                "failed to allocate power gpio\n");
 257                        goto err_power_req;
 258                }
 259                gpio_direction_output(tegra_host->power_gpio, 1);
 260        }
 261
 262        clk = clk_get(mmc_dev(host->mmc), NULL);
 263        if (IS_ERR(clk)) {
 264                dev_err(mmc_dev(host->mmc), "clk err\n");
 265                rc = PTR_ERR(clk);
 266                goto err_clk_get;
 267        }
 268        clk_prepare_enable(clk);
 269        pltfm_host->clk = clk;
 270
 271        rc = sdhci_add_host(host);
 272        if (rc)
 273                goto err_add_host;
 274
 275        return 0;
 276
 277err_add_host:
 278        clk_disable_unprepare(pltfm_host->clk);
 279        clk_put(pltfm_host->clk);
 280err_clk_get:
 281        if (gpio_is_valid(tegra_host->power_gpio))
 282                gpio_free(tegra_host->power_gpio);
 283err_power_req:
 284err_parse_dt:
 285err_alloc_tegra_host:
 286        sdhci_pltfm_free(pdev);
 287        return rc;
 288}
 289
 290static int sdhci_tegra_remove(struct platform_device *pdev)
 291{
 292        struct sdhci_host *host = platform_get_drvdata(pdev);
 293        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 294        struct sdhci_tegra *tegra_host = pltfm_host->priv;
 295        int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
 296
 297        sdhci_remove_host(host, dead);
 298
 299        if (gpio_is_valid(tegra_host->power_gpio))
 300                gpio_free(tegra_host->power_gpio);
 301
 302        clk_disable_unprepare(pltfm_host->clk);
 303        clk_put(pltfm_host->clk);
 304
 305        sdhci_pltfm_free(pdev);
 306
 307        return 0;
 308}
 309
 310static struct platform_driver sdhci_tegra_driver = {
 311        .driver         = {
 312                .name   = "sdhci-tegra",
 313                .owner  = THIS_MODULE,
 314                .of_match_table = sdhci_tegra_dt_match,
 315                .pm     = SDHCI_PLTFM_PMOPS,
 316        },
 317        .probe          = sdhci_tegra_probe,
 318        .remove         = sdhci_tegra_remove,
 319};
 320
 321module_platform_driver(sdhci_tegra_driver);
 322
 323MODULE_DESCRIPTION("SDHCI driver for Tegra");
 324MODULE_AUTHOR("Google, Inc.");
 325MODULE_LICENSE("GPL v2");
 326