sh_mobile_sdhi.c 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * SuperH Mobile SDHI
 *
 * Copyright (C) 2009 Magnus Damm
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Based on "Compaq ASIC3 support":
 *
 * Copyright 2001 Compaq Computer Corporation.
 * Copyright 2004-2005 Phil Blundell
 * Copyright 2007-2008 OpenedHand Ltd.
 *
 * Authors: Phil Blundell <pb@handhelds.org>,
 *	    Samuel Ortiz <sameo@openedhand.com>
 *
 */

#include <linux/kernel.h>
#include <linux/clk.h>
23
#include <linux/slab.h>
24
#include <linux/platform_device.h>
25
#include <linux/mmc/host.h>
26 27
#include <linux/mfd/core.h>
#include <linux/mfd/tmio.h>
28
#include <linux/mfd/sh_mobile_sdhi.h>
29
#include <linux/sh_dma.h>
30 31 32 33 34

struct sh_mobile_sdhi {
	struct clk *clk;
	struct tmio_mmc_data mmc_data;
	struct mfd_cell cell_mmc;
35 36 37
	struct sh_dmae_slave param_tx;
	struct sh_dmae_slave param_rx;
	struct tmio_mmc_dma dma_priv;
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
};

static struct resource sh_mobile_sdhi_resources[] = {
	{
		.start = 0x000,
		.end   = 0x1ff,
		.flags = IORESOURCE_MEM,
	},
	{
		.start = 0,
		.end   = 0,
		.flags = IORESOURCE_IRQ,
	},
};

static struct mfd_cell sh_mobile_sdhi_cell = {
	.name          = "tmio-mmc",
	.num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources),
	.resources     = sh_mobile_sdhi_resources,
};

59 60 61 62 63 64 65 66 67
static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
{
	struct platform_device *pdev = to_platform_device(tmio->dev.parent);
	struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;

	if (p && p->set_pwr)
		p->set_pwr(pdev, state);
}

68 69 70 71 72 73 74 75 76 77 78
static int sh_mobile_sdhi_get_cd(struct platform_device *tmio)
{
	struct platform_device *pdev = to_platform_device(tmio->dev.parent);
	struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;

	if (p && p->get_cd)
		return p->get_cd(pdev);
	else
		return -ENOSYS;
}

79
static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
80 81
{
	struct sh_mobile_sdhi *priv;
82 83
	struct tmio_mmc_data *mmc_data;
	struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	struct resource *mem;
	char clk_name[8];
	int ret, irq;

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!mem)
		dev_err(&pdev->dev, "missing MEM resource\n");

	irq = platform_get_irq(pdev, 0);
	if (irq < 0)
		dev_err(&pdev->dev, "missing IRQ resource\n");

	if (!mem || (irq < 0))
		return -EINVAL;

	priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
	if (priv == NULL) {
		dev_err(&pdev->dev, "kzalloc failed\n");
		return -ENOMEM;
	}

105 106
	mmc_data = &priv->mmc_data;

107 108 109 110 111 112 113 114 115 116 117
	snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
	priv->clk = clk_get(&pdev->dev, clk_name);
	if (IS_ERR(priv->clk)) {
		dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
		ret = PTR_ERR(priv->clk);
		kfree(priv);
		return ret;
	}

	clk_enable(priv->clk);

118 119
	mmc_data->hclk = clk_get_rate(priv->clk);
	mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
120
	mmc_data->get_cd = sh_mobile_sdhi_get_cd;
121
	mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
122
	if (p) {
123
		mmc_data->flags = p->tmio_flags;
124
		mmc_data->ocr_mask = p->tmio_ocr_mask;
125
		mmc_data->capabilities |= p->tmio_caps;
126
	}
127 128 129 130 131 132 133 134

	if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) {
		priv->param_tx.slave_id = p->dma_slave_tx;
		priv->param_rx.slave_id = p->dma_slave_rx;
		priv->dma_priv.chan_priv_tx = &priv->param_tx;
		priv->dma_priv.chan_priv_rx = &priv->param_rx;
		mmc_data->dma = &priv->dma_priv;
	}
135 136

	memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc));
137
	priv->cell_mmc.driver_data = mmc_data;
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	priv->cell_mmc.platform_data = &priv->cell_mmc;
	priv->cell_mmc.data_size = sizeof(priv->cell_mmc);

	platform_set_drvdata(pdev, priv);

	ret = mfd_add_devices(&pdev->dev, pdev->id,
			      &priv->cell_mmc, 1, mem, irq);
	if (ret) {
		clk_disable(priv->clk);
		clk_put(priv->clk);
		kfree(priv);
	}

	return ret;
}

static int sh_mobile_sdhi_remove(struct platform_device *pdev)
{
	struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev);

	mfd_remove_devices(&pdev->dev);
	clk_disable(priv->clk);
	clk_put(priv->clk);
	kfree(priv);

	return 0;
}

static struct platform_driver sh_mobile_sdhi_driver = {
	.driver		= {
		.name	= "sh_mobile_sdhi",
		.owner	= THIS_MODULE,
	},
	.probe		= sh_mobile_sdhi_probe,
	.remove		= __devexit_p(sh_mobile_sdhi_remove),
};

static int __init sh_mobile_sdhi_init(void)
{
	return platform_driver_register(&sh_mobile_sdhi_driver);
}

static void __exit sh_mobile_sdhi_exit(void)
{
	platform_driver_unregister(&sh_mobile_sdhi_driver);
}

module_init(sh_mobile_sdhi_init);
module_exit(sh_mobile_sdhi_exit);

MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL v2");