⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 samsung s3c24xx sdmmc driver.c

📁 Samsung S3C24xx SD/MMC 驱动 This a MMC/SD driver for the Samsung S3C24xx SD/MMC controller, originall
💻 C
📖 第 1 页 / 共 3 页
字号:
+	/* clear command, data and fifo status registers;
+	 * Fifo clear only necessary on 2440, but doesn't hurt on 2410 */
+	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
+	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
+	writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
+
+	if (cmd->data) {
+		int res;
+		res = s3cmci_setup_data(host, cmd->data);
+
+		host->dcnt++;
+
+		if (res) {
+			cmd->error = -EIO;
+			cmd->data->error = -EIO;
+
+			mmc_request_done(mmc, mrq);
+			return;
+		}
+
+
+		if (host->dodma)
+			res = s3cmci_prepare_dma(host, cmd->data);
+		else
+			res = s3cmci_prepare_pio(host, cmd->data);
+
+		if (res) {
+			cmd->error = -EIO;
+			cmd->data->error = -EIO;
+
+			mmc_request_done(mmc, mrq);
+			return;
+		}
+
+	}
+
+	s3cmci_send_command(host, cmd);
+	enable_irq(host->irq);
+}
+
+static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+
+	host->cmd_is_stop = 0;
+	host->mrq = mrq;
+
+	s3cmci_send_request(mmc);
+}
+
+static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+	u32 mci_psc, mci_con;
+
+	/* Set power */
+	mci_con = readl(host->base + S3C2410_SDICON);
+	switch (ios->power_mode) {
+	case MMC_POWER_ON:
+	case MMC_POWER_UP:
+		s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
+		s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
+		s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
+		s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
+		s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+		s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+
+		if (host->pdata->set_power)
+			host->pdata->set_power(ios->power_mode, ios->vdd);
+
+		if (!host->is2440)
+			mci_con |= S3C2410_SDICON_FIFORESET;
+
+		break;
+
+	case MMC_POWER_OFF:
+	default:
+		s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+		s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+
+		if (host->pdata->set_power)
+			host->pdata->set_power(ios->power_mode, ios->vdd);
+
+		if (host->is2440)
+			mci_con |= S3C2440_SDICON_SDRESET;
+
+		break;
+	}
+
+	/* Set clock */
+	for (mci_psc = 0; mci_psc < 255; mci_psc++) {
+		host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
+
+		if (host->real_rate <= ios->clock)
+			break;
+	}
+
+	if (mci_psc > 255)
+		mci_psc = 255;
+	host->prescaler = mci_psc;
+
+	writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+	/* If requested clock is 0, real_rate will be 0, too */
+	if (ios->clock == 0)
+		host->real_rate = 0;
+
+	/* Set CLOCK_ENABLE */
+	if (ios->clock)
+		mci_con |= S3C2410_SDICON_CLOCKTYPE;
+	else
+		mci_con &= ~S3C2410_SDICON_CLOCKTYPE;
+
+	writel(mci_con, host->base + S3C2410_SDICON);
+
+	if ((ios->power_mode == MMC_POWER_ON)
+		|| (ios->power_mode == MMC_POWER_UP)) {
+
+		dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
+		    host->real_rate/1000, ios->clock/1000);
+	} else {
+		dbg(host, dbg_conf, "powered down.\n");
+	}
+
+	host->bus_width = ios->bus_width;
+
+}
+
+static void s3cmci_reset(struct s3cmci_host *host)
+{
+	u32 con = readl(host->base + S3C2410_SDICON);
+
+	con |= S3C2440_SDICON_SDRESET;
+
+	writel(con, host->base + S3C2410_SDICON);
+}
+
+static int s3cmci_get_ro(struct mmc_host *mmc)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+
+	if (host->pdata->gpio_wprotect == 0)
+		return 0;
+
+	return s3c2410_gpio_getpin(host->pdata->gpio_wprotect);
+}
+
+static struct mmc_host_ops s3cmci_ops = {
+	.request	= s3cmci_request,
+	.set_ios	= s3cmci_set_ios,
+	.get_ro		= s3cmci_get_ro,
+};
+
+static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
+	.gpio_detect	= 0,
+	.set_power	= NULL,
+	.ocr_avail	= MMC_VDD_32_33,
+};
+
+static int s3cmci_probe(struct platform_device *pdev, int is2440)
+{
+	struct mmc_host 	*mmc;
+	struct s3cmci_host 	*host;
+
+	int ret;
+
+	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->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) {
+		host->is2440	= 1;
+		host->sdiimsk	= S3C2440_SDIIMSK;
+		host->sdidata	= S3C2440_SDIDATA;
+		host->clk_div	= 1;
+	} else {
+		host->is2440	= 0;
+		host->sdiimsk	= S3C2410_SDIIMSK;
+		host->sdidata	= S3C2410_SDIDATA;
+		host->clk_div	= 2;
+	}
+	host->dodma		= 0;
+	host->complete_what 	= COMPLETION_NONE;
+	host->pio_active 	= XFER_NONE;
+
+	host->dma		= S3CMCI_DMA;
+	host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
+	s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
+
+	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!host->mem) {
+		dev_err(&pdev->dev,
+			"failed to get io memory region resouce.\n");
+
+		ret = -ENOENT;
+		goto probe_free_host;
+	}
+
+	host->mem = request_mem_region(host->mem->start,
+		RESSIZE(host->mem), pdev->name);
+
+	if (!host->mem) {
+		dev_err(&pdev->dev, "failed to request io memory region.\n");
+		ret = -ENOENT;
+		goto probe_free_host;
+	}
+
+	host->base = ioremap(host->mem->start, RESSIZE(host->mem));
+	if (host->base == 0) {
+		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
+		ret = -EINVAL;
+		goto probe_free_mem_region;
+	}
+
+	host->irq = platform_get_irq(pdev, 0);
+	if (host->irq == 0) {
+		dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
+		ret = -EINVAL;
+		goto probe_iounmap;
+	}
+
+	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
+		dev_err(&pdev->dev, "failed to request mci interrupt.\n");
+		ret = -ENOENT;
+		goto probe_iounmap;
+	}
+
+	disable_irq(host->irq);
+
+	s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
+	set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+
+	if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
+		dev_err(&pdev->dev,
+			"failed to request card detect interrupt.\n");
+
+		ret = -ENOENT;
+		goto probe_free_irq;
+	}
+
+	if (host->pdata->gpio_wprotect)
+		s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
+				    S3C2410_GPIO_INPUT);
+
+	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+		dev_err(&pdev->dev, "unable to get DMA channel.\n");
+		ret = -EBUSY;
+		goto probe_free_irq_cd;
+	}
+
+	host->clk = clk_get(&pdev->dev, "sdi");
+	if (IS_ERR(host->clk)) {
+		dev_err(&pdev->dev, "failed to find clock source.\n");
+		ret = PTR_ERR(host->clk);
+		host->clk = NULL;
+		goto probe_free_host;
+	}
+
+	ret = clk_enable(host->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable clock source.\n");
+		goto clk_free;
+	}
+
+	host->clk_rate = clk_get_rate(host->clk);
+
+	mmc->ops 	= &s3cmci_ops;
+	mmc->ocr_avail	= host->pdata->ocr_avail;
+	mmc->caps	= MMC_CAP_4_BIT_DATA;
+	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);
+	mmc->f_max 	= host->clk_rate / host->clk_div;
+
+	mmc->max_blk_count	= 4095;
+	mmc->max_blk_size	= 4095;
+	mmc->max_req_size	= 4095 * 512;
+	mmc->max_seg_size	= mmc->max_req_size;
+
+	mmc->max_phys_segs	= 128;
+	mmc->max_hw_segs	= 128;
+
+	dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u "
+	    "irq_cd:%u dma:%u.\n", (host->is2440?"2440":""),
+	    host->base, host->irq, host->irq_cd, host->dma);
+
+	ret = mmc_add_host(mmc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add mmc host.\n");
+		goto free_dmabuf;
+	}
+
+	platform_set_drvdata(pdev, mmc);
+
+	dev_info(&pdev->dev, "initialisation done.\n");
+	return 0;
+
+ free_dmabuf:
+	clk_disable(host->clk);
+
+ clk_free:
+	clk_put(host->clk);
+
+ probe_free_irq_cd:
+	free_irq(host->irq_cd, host);
+
+ probe_free_irq:
+	free_irq(host->irq, host);
+
+ probe_iounmap:
+	iounmap(host->base);
+
+ probe_free_mem_region:
+	release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ probe_free_host:
+	mmc_free_host(mmc);
+ probe_out:
+	return ret;
+}
+
+static int s3cmci_remove(struct platform_device *pdev)
+{
+	struct mmc_host 	*mmc  = platform_get_drvdata(pdev);
+	struct s3cmci_host 	*host = mmc_priv(mmc);
+
+	mmc_remove_host(mmc);
+	clk_disable(host->clk);
+	clk_put(host->clk);
+	s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
+	free_irq(host->irq_cd, host);
+	free_irq(host->irq, host);
+	iounmap(host->base);
+	release_mem_region(host->mem->start, RESSIZE(host->mem));
+	mmc_free_host(mmc);
+
+	return 0;
+}
+
+static int s3cmci_probe_2410(struct platform_device *dev)
+{
+	return s3cmci_probe(dev, 0);
+}
+
+static int s3cmci_probe_2412(struct platform_device *dev)
+{
+	return s3cmci_probe(dev, 1);
+}
+
+static int s3cmci_probe_2440(struct platform_device *dev)
+{
+	return s3cmci_probe(dev, 1);
+}
+
+#ifdef CONFIG_PM
+
+static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+
+	return  mmc_suspend_host(mmc, state);
+}
+
+static int s3cmci_resume(struct platform_device *dev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+
+	return mmc_resume_host(mmc);
+}
+
+#else /* CONFIG_PM */
+#define s3cmci_suspend NULL
+#define s3cmci_resume NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver s3cmci_driver_2410 = {
+	.driver.name	= "s3c2410-sdi",
+	.probe		= s3cmci_probe_2410,
+	.remove		= s3cmci_remove,
+	.suspend	= s3cmci_suspend,
+	.resume		= s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_driver_2412 = {
+	.driver.name	= "s3c2412-sdi",
+	.probe		= s3cmci_probe_2412,
+	.remove		= s3cmci_remove,
+	.suspend	= s3cmci_suspend,
+	.resume		= s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_driver_2440 = {
+	.driver.name	= "s3c2440-sdi",
+	.probe		= s3cmci_probe_2440,
+	.remove		= s3cmci_remove,
+	.suspend	= s3cmci_suspend,
+	.resume		= s3cmci_resume,
+};
+
+
+static int __init s3cmci_init(void)
+{
+	platform_driver_register(&s3cmci_driver_2410);
+	platform_driver_register(&s3cmci_driver_2412);
+	platform_driver_register(&s3cmci_driver_2440);
+	return 0;
+}
+
+static void __exit s3cmci_exit(void)
+{
+	platform_driver_unregister(&s3cmci_driver_2410);
+	platform_driver_unregister(&s3cmci_driver_2412);
+	platform_driver_unregister(&s3cmci_driver_2440);
+}
+
+module_init(s3cmci_init);
+module_exit(s3cmci_exit);
+
+MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
+
Index: linux-2.6/drivers/mmc/host/s3cmci.h
===================================================================
--- /dev/null
+++ linux-2.6/drivers/mmc/host/s3cmci.h
@@ -0,0 +1,71 @@
+/*
+ *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ *  Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
+ *
+ * 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.
+ */
+
+/* FIXME: DMA Resource management ?! */
+#define S3CMCI_DMA 0
+
+enum s3cmci_waitfor {
+	COMPLETION_NONE,
+	COMPLETION_FINALIZE,
+	COMPLETION_CMDSENT,
+	COMPLETION_RSPFIN,
+	COMPLETION_XFERFINISH,
+	COMPLETION_XFERFINISH_RSPFIN,
+};
+
+struct s3cmci_host {
+	struct platform_device	*pdev;
+	struct s3c24xx_mci_pdata *pdata;
+	struct mmc_host		*mmc;
+	struct resource		*mem;
+	struct clk		*clk;
+	void __iomem		*base;
+	int			irq;
+	int			irq_cd;
+	int			dma;
+
+	unsigned long		clk_rate;
+	unsigned long		clk_div;
+	unsigned long		real_rate;
+	u8			prescaler;
+
+	int			is2440;
+	unsigned		sdiimsk;
+	unsigned		sdidata;
+	int			dodma;
+
+	int			dmatogo;
+
+	struct mmc_request	*mrq;
+	int			cmd_is_stop;
+
+	spinlock_t		complete_lock;
+	enum s3cmci_waitfor	complete_what;
+
+	int			dma_complete;
+
+	u32			pio_sgptr;
+	u32			pio_words;
+	u32			pio_count;
+	u32			*pio_ptr;
+#define XFER_NONE 0
+#define XFER_READ 1
+#define XFER_WRITE 2
+	u32			pio_active;
+
+	int			bus_width;
+
+	char 			dbgmsg_cmd[301];
+	char 			dbgmsg_dat[301];
+	char			*status;
+
+	unsigned int		ccnt, dcnt;
+	struct tasklet_struct	pio_tasklet;
+};
Index: linux-2.6/drivers/mmc/host/Kconfig
===================================================================
--- linux-2.6.orig/drivers/mmc/host/Kconfig
+++ linux-2.6/drivers/mmc/host/Kconfig
@@ -130,3 +130,14 @@
 
 	  If unsure, or if your system has no SPI master driver, say N.
 
+config MMC_S3C
+	tristate "Samsung S3C24xx SD/MMC Card Interface support"
+	depends on ARCH_S3C2410 && MMC
+	help
+	  This selects a driver for the MCI interface found in
+	  Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
+	  If you have a board based on one of those and a MMC/SD
+	  slot, say Y or M here.
+
+	  If unsure, say N.
+
Index: linux-2.6/drivers/mmc/host/Makefile
===================================================================
--- linux-2.6.orig/drivers/mmc/host/Makefile
+++ linux-2.6/drivers/mmc/host/Makefile
@@ -17,4 +17,4 @@
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
 obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -