linux/drivers/mmc/host/sh_mobile_sdhi.c
<<
>>
Prefs
   1/*
   2 * SuperH Mobile SDHI
   3 *
   4 * Copyright (C) 2009 Magnus Damm
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Based on "Compaq ASIC3 support":
  11 *
  12 * Copyright 2001 Compaq Computer Corporation.
  13 * Copyright 2004-2005 Phil Blundell
  14 * Copyright 2007-2008 OpenedHand Ltd.
  15 *
  16 * Authors: Phil Blundell <pb@handhelds.org>,
  17 *          Samuel Ortiz <sameo@openedhand.com>
  18 *
  19 */
  20
  21#include <linux/kernel.h>
  22#include <linux/clk.h>
  23#include <linux/slab.h>
  24#include <linux/mod_devicetable.h>
  25#include <linux/module.h>
  26#include <linux/of_device.h>
  27#include <linux/platform_device.h>
  28#include <linux/mmc/host.h>
  29#include <linux/mmc/sh_mobile_sdhi.h>
  30#include <linux/mfd/tmio.h>
  31#include <linux/sh_dma.h>
  32#include <linux/delay.h>
  33
  34#include "tmio_mmc.h"
  35
  36struct sh_mobile_sdhi_of_data {
  37        unsigned long tmio_flags;
  38};
  39
  40static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
  41        {
  42                .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
  43        },
  44};
  45
  46struct sh_mobile_sdhi {
  47        struct clk *clk;
  48        struct tmio_mmc_data mmc_data;
  49        struct sh_dmae_slave param_tx;
  50        struct sh_dmae_slave param_rx;
  51        struct tmio_mmc_dma dma_priv;
  52};
  53
  54static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
  55{
  56        struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
  57        struct tmio_mmc_host *host = mmc_priv(mmc);
  58        struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
  59        int ret = clk_enable(priv->clk);
  60        if (ret < 0)
  61                return ret;
  62
  63        *f = clk_get_rate(priv->clk);
  64        return 0;
  65}
  66
  67static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
  68{
  69        struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
  70        struct tmio_mmc_host *host = mmc_priv(mmc);
  71        struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
  72        clk_disable(priv->clk);
  73}
  74
  75static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state)
  76{
  77        struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
  78
  79        p->set_pwr(pdev, state);
  80}
  81
  82static int sh_mobile_sdhi_get_cd(struct platform_device *pdev)
  83{
  84        struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
  85
  86        return p->get_cd(pdev);
  87}
  88
  89static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
  90{
  91        int timeout = 1000;
  92
  93        while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13)))
  94                udelay(1);
  95
  96        if (!timeout) {
  97                dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
  98                return -EBUSY;
  99        }
 100
 101        return 0;
 102}
 103
 104static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
 105{
 106        switch (addr)
 107        {
 108        case CTL_SD_CMD:
 109        case CTL_STOP_INTERNAL_ACTION:
 110        case CTL_XFER_BLK_COUNT:
 111        case CTL_SD_CARD_CLK_CTL:
 112        case CTL_SD_XFER_LEN:
 113        case CTL_SD_MEM_CARD_OPT:
 114        case CTL_TRANSACTION_CTL:
 115        case CTL_DMA_ENABLE:
 116                return sh_mobile_sdhi_wait_idle(host);
 117        }
 118
 119        return 0;
 120}
 121
 122static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
 123{
 124        mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100));
 125}
 126
 127static const struct sh_mobile_sdhi_ops sdhi_ops = {
 128        .cd_wakeup = sh_mobile_sdhi_cd_wakeup,
 129};
 130
 131static const struct of_device_id sh_mobile_sdhi_of_match[] = {
 132        { .compatible = "renesas,shmobile-sdhi" },
 133        { .compatible = "renesas,sh7372-sdhi" },
 134        { .compatible = "renesas,r8a7740-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
 135        {},
 136};
 137MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
 138
 139static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 140{
 141        const struct of_device_id *of_id =
 142                of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
 143        struct sh_mobile_sdhi *priv;
 144        struct tmio_mmc_data *mmc_data;
 145        struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
 146        struct tmio_mmc_host *host;
 147        int irq, ret, i = 0;
 148        bool multiplexed_isr = true;
 149
 150        priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
 151        if (priv == NULL) {
 152                dev_err(&pdev->dev, "kzalloc failed\n");
 153                return -ENOMEM;
 154        }
 155
 156        mmc_data = &priv->mmc_data;
 157
 158        if (p) {
 159                if (p->init) {
 160                        ret = p->init(pdev, &sdhi_ops);
 161                        if (ret)
 162                                return ret;
 163                }
 164        }
 165
 166        priv->clk = devm_clk_get(&pdev->dev, NULL);
 167        if (IS_ERR(priv->clk)) {
 168                ret = PTR_ERR(priv->clk);
 169                dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
 170                goto eclkget;
 171        }
 172
 173        mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
 174        mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
 175        mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
 176        mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
 177        if (p) {
 178                mmc_data->flags = p->tmio_flags;
 179                mmc_data->ocr_mask = p->tmio_ocr_mask;
 180                mmc_data->capabilities |= p->tmio_caps;
 181                mmc_data->capabilities2 |= p->tmio_caps2;
 182                mmc_data->cd_gpio = p->cd_gpio;
 183                if (p->set_pwr)
 184                        mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
 185                if (p->get_cd)
 186                        mmc_data->get_cd = sh_mobile_sdhi_get_cd;
 187
 188                if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
 189                        priv->param_tx.shdma_slave.slave_id = p->dma_slave_tx;
 190                        priv->param_rx.shdma_slave.slave_id = p->dma_slave_rx;
 191                        priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave;
 192                        priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave;
 193                        priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */
 194                        mmc_data->dma = &priv->dma_priv;
 195                }
 196        }
 197
 198        /*
 199         * All SDHI blocks support 2-byte and larger block sizes in 4-bit
 200         * bus width mode.
 201         */
 202        mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES;
 203
 204        /*
 205         * All SDHI blocks support SDIO IRQ signalling.
 206         */
 207        mmc_data->flags |= TMIO_MMC_SDIO_IRQ;
 208
 209        if (of_id && of_id->data) {
 210                const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
 211                mmc_data->flags |= of_data->tmio_flags;
 212        }
 213
 214        ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
 215        if (ret < 0)
 216                goto eprobe;
 217
 218        /*
 219         * Allow one or more specific (named) ISRs or
 220         * one or more multiplexed (un-named) ISRs.
 221         */
 222
 223        irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
 224        if (irq >= 0) {
 225                multiplexed_isr = false;
 226                ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_card_detect_irq, 0,
 227                                  dev_name(&pdev->dev), host);
 228                if (ret)
 229                        goto eirq;
 230        }
 231
 232        irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
 233        if (irq >= 0) {
 234                multiplexed_isr = false;
 235                ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdio_irq, 0,
 236                                  dev_name(&pdev->dev), host);
 237                if (ret)
 238                        goto eirq;
 239        }
 240
 241        irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
 242        if (irq >= 0) {
 243                multiplexed_isr = false;
 244                ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdcard_irq, 0,
 245                                  dev_name(&pdev->dev), host);
 246                if (ret)
 247                        goto eirq;
 248        } else if (!multiplexed_isr) {
 249                dev_err(&pdev->dev,
 250                        "Principal SD-card IRQ is missing among named interrupts\n");
 251                ret = irq;
 252                goto eirq;
 253        }
 254
 255        if (multiplexed_isr) {
 256                while (1) {
 257                        irq = platform_get_irq(pdev, i);
 258                        if (irq < 0)
 259                                break;
 260                        i++;
 261                        ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0,
 262                                          dev_name(&pdev->dev), host);
 263                        if (ret)
 264                                goto eirq;
 265                }
 266
 267                /* There must be at least one IRQ source */
 268                if (!i)
 269                        goto eirq;
 270        }
 271
 272        dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
 273                 mmc_hostname(host->mmc), (unsigned long)
 274                 (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
 275                 host->mmc->f_max / 1000000);
 276
 277        return ret;
 278
 279eirq:
 280        tmio_mmc_host_remove(host);
 281eprobe:
 282eclkget:
 283        if (p && p->cleanup)
 284                p->cleanup(pdev);
 285        return ret;
 286}
 287
 288static int sh_mobile_sdhi_remove(struct platform_device *pdev)
 289{
 290        struct mmc_host *mmc = platform_get_drvdata(pdev);
 291        struct tmio_mmc_host *host = mmc_priv(mmc);
 292        struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
 293
 294        tmio_mmc_host_remove(host);
 295
 296        if (p && p->cleanup)
 297                p->cleanup(pdev);
 298
 299        return 0;
 300}
 301
 302static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
 303        .suspend = tmio_mmc_host_suspend,
 304        .resume = tmio_mmc_host_resume,
 305        .runtime_suspend = tmio_mmc_host_runtime_suspend,
 306        .runtime_resume = tmio_mmc_host_runtime_resume,
 307};
 308
 309static struct platform_driver sh_mobile_sdhi_driver = {
 310        .driver         = {
 311                .name   = "sh_mobile_sdhi",
 312                .owner  = THIS_MODULE,
 313                .pm     = &tmio_mmc_dev_pm_ops,
 314                .of_match_table = sh_mobile_sdhi_of_match,
 315        },
 316        .probe          = sh_mobile_sdhi_probe,
 317        .remove         = sh_mobile_sdhi_remove,
 318};
 319
 320module_platform_driver(sh_mobile_sdhi_driver);
 321
 322MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
 323MODULE_AUTHOR("Magnus Damm");
 324MODULE_LICENSE("GPL v2");
 325MODULE_ALIAS("platform:sh_mobile_sdhi");
 326
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.