linux/drivers/mmc/host/sdhci_f_sdh30.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * linux/drivers/mmc/host/sdhci_f_sdh30.c
   4 *
   5 * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
   6 *              Vincent Yang <vincent.yang@tw.fujitsu.com>
   7 * Copyright (C) 2015 Linaro Ltd  Andy Green <andy.green@linaro.org>
   8 */
   9
  10#include <linux/acpi.h>
  11#include <linux/err.h>
  12#include <linux/delay.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/property.h>
  16#include <linux/clk.h>
  17
  18#include "sdhci-pltfm.h"
  19
  20/* F_SDH30 extended Controller registers */
  21#define F_SDH30_AHB_CONFIG              0x100
  22#define  F_SDH30_AHB_BIGED              0x00000040
  23#define  F_SDH30_BUSLOCK_DMA            0x00000020
  24#define  F_SDH30_BUSLOCK_EN             0x00000010
  25#define  F_SDH30_SIN                    0x00000008
  26#define  F_SDH30_AHB_INCR_16            0x00000004
  27#define  F_SDH30_AHB_INCR_8             0x00000002
  28#define  F_SDH30_AHB_INCR_4             0x00000001
  29
  30#define F_SDH30_TUNING_SETTING          0x108
  31#define  F_SDH30_CMD_CHK_DIS            0x00010000
  32
  33#define F_SDH30_IO_CONTROL2             0x114
  34#define  F_SDH30_CRES_O_DN              0x00080000
  35#define  F_SDH30_MSEL_O_1_8             0x00040000
  36
  37#define F_SDH30_ESD_CONTROL             0x124
  38#define  F_SDH30_EMMC_RST               0x00000002
  39#define  F_SDH30_EMMC_HS200             0x01000000
  40
  41#define F_SDH30_CMD_DAT_DELAY           0x200
  42
  43#define F_SDH30_MIN_CLOCK               400000
  44
  45struct f_sdhost_priv {
  46        struct clk *clk_iface;
  47        struct clk *clk;
  48        u32 vendor_hs200;
  49        struct device *dev;
  50        bool enable_cmd_dat_delay;
  51};
  52
  53static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
  54{
  55        struct f_sdhost_priv *priv = sdhci_priv(host);
  56        u32 ctrl = 0;
  57
  58        usleep_range(2500, 3000);
  59        ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
  60        ctrl |= F_SDH30_CRES_O_DN;
  61        sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
  62        ctrl |= F_SDH30_MSEL_O_1_8;
  63        sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
  64
  65        ctrl &= ~F_SDH30_CRES_O_DN;
  66        sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
  67        usleep_range(2500, 3000);
  68
  69        if (priv->vendor_hs200) {
  70                dev_info(priv->dev, "%s: setting hs200\n", __func__);
  71                ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
  72                ctrl |= priv->vendor_hs200;
  73                sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
  74        }
  75
  76        ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
  77        ctrl |= F_SDH30_CMD_CHK_DIS;
  78        sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
  79}
  80
  81static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
  82{
  83        return F_SDH30_MIN_CLOCK;
  84}
  85
  86static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
  87{
  88        struct f_sdhost_priv *priv = sdhci_priv(host);
  89        u32 ctl;
  90
  91        if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
  92                sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
  93
  94        sdhci_reset(host, mask);
  95
  96        if (priv->enable_cmd_dat_delay) {
  97                ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
  98                ctl |= F_SDH30_CMD_DAT_DELAY;
  99                sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
 100        }
 101}
 102
 103static const struct sdhci_ops sdhci_f_sdh30_ops = {
 104        .voltage_switch = sdhci_f_sdh30_soft_voltage_switch,
 105        .get_min_clock = sdhci_f_sdh30_get_min_clock,
 106        .reset = sdhci_f_sdh30_reset,
 107        .set_clock = sdhci_set_clock,
 108        .set_bus_width = sdhci_set_bus_width,
 109        .set_uhs_signaling = sdhci_set_uhs_signaling,
 110};
 111
 112static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 113{
 114        struct sdhci_host *host;
 115        struct device *dev = &pdev->dev;
 116        struct resource *res;
 117        int irq, ctrl = 0, ret = 0;
 118        struct f_sdhost_priv *priv;
 119        u32 reg = 0;
 120
 121        irq = platform_get_irq(pdev, 0);
 122        if (irq < 0)
 123                return irq;
 124
 125        host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
 126        if (IS_ERR(host))
 127                return PTR_ERR(host);
 128
 129        priv = sdhci_priv(host);
 130        priv->dev = dev;
 131
 132        host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
 133                       SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
 134        host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
 135                        SDHCI_QUIRK2_TUNING_WORK_AROUND;
 136
 137        priv->enable_cmd_dat_delay = device_property_read_bool(dev,
 138                                                "fujitsu,cmd-dat-delay-select");
 139
 140        ret = mmc_of_parse(host->mmc);
 141        if (ret)
 142                goto err;
 143
 144        platform_set_drvdata(pdev, host);
 145
 146        host->hw_name = "f_sdh30";
 147        host->ops = &sdhci_f_sdh30_ops;
 148        host->irq = irq;
 149
 150        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 151        host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
 152        if (IS_ERR(host->ioaddr)) {
 153                ret = PTR_ERR(host->ioaddr);
 154                goto err;
 155        }
 156
 157        if (dev_of_node(dev)) {
 158                sdhci_get_of_property(pdev);
 159
 160                priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
 161                if (IS_ERR(priv->clk_iface)) {
 162                        ret = PTR_ERR(priv->clk_iface);
 163                        goto err;
 164                }
 165
 166                ret = clk_prepare_enable(priv->clk_iface);
 167                if (ret)
 168                        goto err;
 169
 170                priv->clk = devm_clk_get(&pdev->dev, "core");
 171                if (IS_ERR(priv->clk)) {
 172                        ret = PTR_ERR(priv->clk);
 173                        goto err_clk;
 174                }
 175
 176                ret = clk_prepare_enable(priv->clk);
 177                if (ret)
 178                        goto err_clk;
 179        }
 180
 181        /* init vendor specific regs */
 182        ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
 183        ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
 184                F_SDH30_AHB_INCR_4;
 185        ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
 186        sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG);
 187
 188        reg = sdhci_readl(host, F_SDH30_ESD_CONTROL);
 189        sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
 190        msleep(20);
 191        sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
 192
 193        reg = sdhci_readl(host, SDHCI_CAPABILITIES);
 194        if (reg & SDHCI_CAN_DO_8BIT)
 195                priv->vendor_hs200 = F_SDH30_EMMC_HS200;
 196
 197        ret = sdhci_add_host(host);
 198        if (ret)
 199                goto err_add_host;
 200
 201        return 0;
 202
 203err_add_host:
 204        clk_disable_unprepare(priv->clk);
 205err_clk:
 206        clk_disable_unprepare(priv->clk_iface);
 207err:
 208        sdhci_free_host(host);
 209        return ret;
 210}
 211
 212static int sdhci_f_sdh30_remove(struct platform_device *pdev)
 213{
 214        struct sdhci_host *host = platform_get_drvdata(pdev);
 215        struct f_sdhost_priv *priv = sdhci_priv(host);
 216
 217        sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
 218                          0xffffffff);
 219
 220        clk_disable_unprepare(priv->clk_iface);
 221        clk_disable_unprepare(priv->clk);
 222
 223        sdhci_free_host(host);
 224        platform_set_drvdata(pdev, NULL);
 225
 226        return 0;
 227}
 228
 229#ifdef CONFIG_OF
 230static const struct of_device_id f_sdh30_dt_ids[] = {
 231        { .compatible = "fujitsu,mb86s70-sdhci-3.0" },
 232        { /* sentinel */ }
 233};
 234MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids);
 235#endif
 236
 237#ifdef CONFIG_ACPI
 238static const struct acpi_device_id f_sdh30_acpi_ids[] = {
 239        { "SCX0002" },
 240        { /* sentinel */ }
 241};
 242MODULE_DEVICE_TABLE(acpi, f_sdh30_acpi_ids);
 243#endif
 244
 245static struct platform_driver sdhci_f_sdh30_driver = {
 246        .driver = {
 247                .name = "f_sdh30",
 248                .of_match_table = of_match_ptr(f_sdh30_dt_ids),
 249                .acpi_match_table = ACPI_PTR(f_sdh30_acpi_ids),
 250                .pm     = &sdhci_pltfm_pmops,
 251        },
 252        .probe  = sdhci_f_sdh30_probe,
 253        .remove = sdhci_f_sdh30_remove,
 254};
 255
 256module_platform_driver(sdhci_f_sdh30_driver);
 257
 258MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
 259MODULE_LICENSE("GPL v2");
 260MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
 261MODULE_ALIAS("platform:f_sdh30");
 262