Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e584e075 authored by Sergio Prado's avatar Sergio Prado Committed by Ulf Hansson
Browse files

mmc: host: s3cmci: allow probing from device tree



Allows configuring Samsung S3C24XX MMC/SD/SDIO controller using a device
tree.

Signed-off-by: default avatarSergio Prado <sergio.prado@e-labworks.com>
[Arnd: fix broken conditional expression]
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent d5cc7c80
Loading
Loading
Loading
Loading
+126 −131
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h>

#include <plat/gpio-cfg.h>
#include <mach/dma.h>
@@ -807,21 +811,6 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)

}

/*
 * ISR for the CardDetect Pin
*/

static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
{
	struct s3cmci_host *host = (struct s3cmci_host *)dev_id;

	dbg(host, dbg_irq, "card detect\n");

	mmc_detect_change(host->mmc, msecs_to_jiffies(500));

	return IRQ_HANDLED;
}

static void s3cmci_dma_done_callback(void *arg)
{
	struct s3cmci_host *host = arg;
@@ -1177,19 +1166,6 @@ static void s3cmci_send_request(struct mmc_host *mmc)
	s3cmci_enable_irq(host, true);
}

static int s3cmci_card_present(struct mmc_host *mmc)
{
	struct s3cmci_host *host = mmc_priv(mmc);
	struct s3c24xx_mci_pdata *pdata = host->pdata;
	int ret;

	if (pdata->no_detect)
		return -ENOSYS;

	ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1;
	return ret ^ pdata->detect_invert;
}

static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
	struct s3cmci_host *host = mmc_priv(mmc);
@@ -1198,7 +1174,7 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
	host->cmd_is_stop = 0;
	host->mrq = mrq;

	if (s3cmci_card_present(mmc) == 0) {
	if (mmc_gpio_get_cd(mmc) == 0) {
		dbg(host, dbg_err, "%s: no medium present\n", __func__);
		host->mrq->cmd->error = -ENOMEDIUM;
		mmc_request_done(mmc, mrq);
@@ -1242,6 +1218,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
	case MMC_POWER_ON:
	case MMC_POWER_UP:
		/* Configure GPE5...GPE10 pins in SD mode */
		if (!host->pdev->dev.of_node)
			s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),
					      S3C_GPIO_PULL_NONE);

@@ -1255,6 +1232,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

	case MMC_POWER_OFF:
	default:
		if (!host->pdev->dev.of_node)
			gpio_direction_output(S3C2410_GPE(5), 0);

		if (host->is2440)
@@ -1295,21 +1273,6 @@ static void s3cmci_reset(struct s3cmci_host *host)
	writel(con, host->base + S3C2410_SDICON);
}

static int s3cmci_get_ro(struct mmc_host *mmc)
{
	struct s3cmci_host *host = mmc_priv(mmc);
	struct s3c24xx_mci_pdata *pdata = host->pdata;
	int ret;

	if (pdata->no_wprotect)
		return 0;

	ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;
	ret ^= pdata->wprotect_invert;

	return ret;
}

static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
	struct s3cmci_host *host = mmc_priv(mmc);
@@ -1353,8 +1316,8 @@ static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
static struct mmc_host_ops s3cmci_ops = {
	.request	= s3cmci_request,
	.set_ios	= s3cmci_set_ios,
	.get_ro		= s3cmci_get_ro,
	.get_cd		= s3cmci_card_present,
	.get_ro		= mmc_gpio_get_ro,
	.get_cd		= mmc_gpio_get_cd,
	.enable_sdio_irq = s3cmci_enable_sdio_irq,
};

@@ -1545,21 +1508,14 @@ static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { }

#endif /* CONFIG_DEBUG_FS */

static int s3cmci_probe(struct platform_device *pdev)
static int s3cmci_probe_pdata(struct s3cmci_host *host)
{
	struct s3cmci_host *host;
	struct mmc_host	*mmc;
	int ret;
	int is2440;
	int i;

	is2440 = platform_get_device_id(pdev)->driver_data;
	struct platform_device *pdev = host->pdev;
	struct mmc_host *mmc = host->mmc;
	struct s3c24xx_mci_pdata *pdata;
	int i, ret;

	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
	if (!mmc) {
		ret = -ENOMEM;
		goto probe_out;
	}
	host->is2440 = platform_get_device_id(pdev)->driver_data;

	for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {
		ret = gpio_request(i, dev_name(&pdev->dev));
@@ -1569,25 +1525,101 @@ static int s3cmci_probe(struct platform_device *pdev)
			for (i--; i >= S3C2410_GPE(5); i--)
				gpio_free(i);

			goto probe_free_host;
			return ret;
		}
	}

	if (!pdev->dev.platform_data)
		pdev->dev.platform_data = &s3cmci_def_pdata;

	pdata = pdev->dev.platform_data;

	if (pdata->no_wprotect)
		mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;

	if (pdata->no_detect)
		mmc->caps |= MMC_CAP_NEEDS_POLL;

	if (pdata->wprotect_invert)
		mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;

	if (pdata->detect_invert)
		 mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;

	if (gpio_is_valid(pdata->gpio_detect)) {
		ret = mmc_gpio_request_cd(mmc, pdata->gpio_detect, 0);
		if (ret) {
			dev_err(&pdev->dev, "error requesting GPIO for CD %d\n",
				ret);
			return ret;
		}
	}

	if (gpio_is_valid(pdata->gpio_wprotect)) {
		ret = mmc_gpio_request_ro(mmc, pdata->gpio_wprotect);
		if (ret) {
			dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
				ret);
			return ret;
		}
	}

	return 0;
}

static int s3cmci_probe_dt(struct s3cmci_host *host)
{
	struct platform_device *pdev = host->pdev;
	struct s3c24xx_mci_pdata *pdata;
	struct mmc_host *mmc = host->mmc;
	int ret;

	host->is2440 = (int) of_device_get_match_data(&pdev->dev);

	ret = mmc_of_parse(mmc);
	if (ret)
		return ret;

	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	pdev->dev.platform_data = pdata;

	return 0;
}

static int s3cmci_probe(struct platform_device *pdev)
{
	struct s3cmci_host *host;
	struct mmc_host	*mmc;
	int ret;
	int i;

	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
	if (!mmc) {
		ret = -ENOMEM;
		goto probe_out;
	}

	host = mmc_priv(mmc);
	host->mmc 	= mmc;
	host->pdev	= pdev;
	host->is2440	= is2440;

	if (pdev->dev.of_node)
		ret = s3cmci_probe_dt(host);
	else
		ret = s3cmci_probe_pdata(host);

	if (ret)
		goto probe_free_host;

	host->pdata = pdev->dev.platform_data;
	if (!host->pdata) {
		pdev->dev.platform_data = &s3cmci_def_pdata;
		host->pdata = &s3cmci_def_pdata;
	}

	spin_lock_init(&host->complete_lock);
	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);

	if (is2440) {
	if (host->is2440) {
		host->sdiimsk	= S3C2440_SDIIMSK;
		host->sdidata	= S3C2440_SDIDATA;
		host->clk_div	= 1;
@@ -1645,43 +1677,6 @@ static int s3cmci_probe(struct platform_device *pdev)
	disable_irq(host->irq);
	host->irq_state = false;

	if (!host->pdata->no_detect) {
		ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");
		if (ret) {
			dev_err(&pdev->dev, "failed to get detect gpio\n");
			goto probe_free_irq;
		}

		host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);

		if (host->irq_cd >= 0) {
			if (request_irq(host->irq_cd, s3cmci_irq_cd,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING,
					DRIVER_NAME, host)) {
				dev_err(&pdev->dev,
					"can't get card detect irq.\n");
				ret = -ENOENT;
				goto probe_free_gpio_cd;
			}
		} else {
			dev_warn(&pdev->dev,
				 "host detect has no irq available\n");
			gpio_direction_input(host->pdata->gpio_detect);
		}
	} else
		host->irq_cd = -1;

	if (!host->pdata->no_wprotect) {
		ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");
		if (ret) {
			dev_err(&pdev->dev, "failed to get writeprotect\n");
			goto probe_free_irq_cd;
		}

		gpio_direction_input(host->pdata->gpio_wprotect);
	}

	/* Depending on the dma state, get a DMA channel to use. */

	if (s3cmci_host_usedma(host)) {
@@ -1689,7 +1684,7 @@ static int s3cmci_probe(struct platform_device *pdev)
		ret = PTR_ERR_OR_ZERO(host->dma);
		if (ret) {
			dev_err(&pdev->dev, "cannot get DMA channel.\n");
			goto probe_free_gpio_wp;
			goto probe_free_irq;
		}
	}

@@ -1768,18 +1763,6 @@ static int s3cmci_probe(struct platform_device *pdev)
	if (s3cmci_host_usedma(host))
		dma_release_channel(host->dma);

 probe_free_gpio_wp:
	if (!host->pdata->no_wprotect)
		gpio_free(host->pdata->gpio_wprotect);

 probe_free_gpio_cd:
	if (!host->pdata->no_detect)
		gpio_free(host->pdata->gpio_detect);

 probe_free_irq_cd:
	if (host->irq_cd >= 0)
		free_irq(host->irq_cd, host);

 probe_free_irq:
	free_irq(host->irq, host);

@@ -1790,6 +1773,7 @@ static int s3cmci_probe(struct platform_device *pdev)
	release_mem_region(host->mem->start, resource_size(host->mem));

 probe_free_gpio:
	if (!pdev->dev.of_node)
		for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
			gpio_free(i);

@@ -1818,7 +1802,6 @@ static int s3cmci_remove(struct platform_device *pdev)
{
	struct mmc_host		*mmc  = platform_get_drvdata(pdev);
	struct s3cmci_host	*host = mmc_priv(mmc);
	struct s3c24xx_mci_pdata *pd = host->pdata;
	int i;

	s3cmci_shutdown(pdev);
@@ -1832,16 +1815,10 @@ static int s3cmci_remove(struct platform_device *pdev)

	free_irq(host->irq, host);

	if (!pd->no_wprotect)
		gpio_free(pd->gpio_wprotect);

	if (!pd->no_detect)
		gpio_free(pd->gpio_detect);

	if (!pdev->dev.of_node)
		for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
			gpio_free(i);


	iounmap(host->base);
	release_mem_region(host->mem->start, resource_size(host->mem));

@@ -1849,6 +1826,23 @@ static int s3cmci_remove(struct platform_device *pdev)
	return 0;
}

static const struct of_device_id s3cmci_dt_match[] = {
	{
		.compatible = "samsung,s3c2410-sdi",
		.data = (void *)0,
	},
	{
		.compatible = "samsung,s3c2412-sdi",
		.data = (void *)1,
	},
	{
		.compatible = "samsung,s3c2440-sdi",
		.data = (void *)1,
	},
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, s3cmci_dt_match);

static const struct platform_device_id s3cmci_driver_ids[] = {
	{
		.name	= "s3c2410-sdi",
@@ -1868,6 +1862,7 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
static struct platform_driver s3cmci_driver = {
	.driver	= {
		.name	= "s3c-sdi",
		.of_match_table = s3cmci_dt_match,
	},
	.id_table	= s3cmci_driver_ids,
	.probe		= s3cmci_probe,