#include <asm/dma.h>
-#include <asm/arch/regs-sdi.h>
-#include <asm/arch/regs-gpio.h>
+#include <mach/regs-sdi.h>
+#include <mach/regs-gpio.h>
+
+#include <asm/plat-s3c24xx/mci.h>
#include "s3cmci.h"
struct s3cmci_host *host = (struct s3cmci_host *) data;
+ disable_irq(host->irq);
+
if (host->pio_active == XFER_WRITE)
do_pio_write(host);
(host->pio_active == XFER_READ) ? "read" : "write",
host->pio_count, host->pio_words);
- host->mrq->data->error = -EINVAL;
+ if (host->mrq->data)
+ host->mrq->data->error = -EINVAL;
}
- disable_irq(host->irq);
finalize_request(host);
- }
+ } else
+ enable_irq(host->irq);
}
/*
}
if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+ dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
cmd->error = -ETIMEDOUT;
host->status = "error: command timeout";
goto fail_transfer;
/* Check for FIFO failure */
if (host->is2440) {
if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
+ dbg(host, dbg_err, "FIFO failure\n");
host->mrq->data->error = -EILSEQ;
host->status = "error: 2440 fifo failure";
goto fail_transfer;
}
} else {
if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+ dbg(host, dbg_err, "FIFO failure\n");
cmd->data->error = -EILSEQ;
host->status = "error: fifo failure";
goto fail_transfer;
}
if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+ dbg(host, dbg_err, "bad data crc (outgoing)\n");
cmd->data->error = -EILSEQ;
host->status = "error: bad data crc (outgoing)";
goto fail_transfer;
}
if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+ dbg(host, dbg_err, "bad data crc (incoming)\n");
cmd->data->error = -EILSEQ;
host->status = "error: bad data crc (incoming)";
goto fail_transfer;
}
if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+ dbg(host, dbg_err, "data timeout\n");
cmd->data->error = -ETIMEDOUT;
host->status = "error: data timeout";
goto fail_transfer;
dbg(host, dbg_irq, "card detect\n");
- mmc_detect_change(host->mmc, 500);
+ mmc_detect_change(host->mmc, msecs_to_jiffies(500));
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&host->complete_lock, iflags);
return;
-
fail_request:
host->mrq->data->error = -EINVAL;
host->complete_what = COMPLETION_FINALIZE;
/* Cleanup controller */
writel(0, host->base + S3C2410_SDICMDARG);
- writel(0, host->base + S3C2410_SDIDCON);
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
writel(0, host->base + S3C2410_SDICMDCON);
writel(0, host->base + host->sdiimsk);
return 0;
}
+ if ((data->blksz & 3) != 0) {
+ /* We cannot deal with unaligned blocks with more than
+ * one block being transfered. */
+
+ if (data->blocks > 1)
+ return -EINVAL;
+
+ /* No support yet for non-word block transfers. */
+ return -EINVAL;
+ }
+
while (readl(host->base + S3C2410_SDIDSTA) &
(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
dbg(host, dbg_err,
"mci_setup_data() transfer stillin progress.\n");
- writel(0, host->base + S3C2410_SDIDCON);
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
s3cmci_reset(host);
if ((stoptries--) == 0) {
host->dcnt++;
if (res) {
- cmd->error = -EINVAL;
- cmd->data->error = -EINVAL;
+ dbg(host, dbg_err, "setup data error %d\n", res);
+ cmd->error = res;
+ cmd->data->error = res;
mmc_request_done(mmc, mrq);
return;
res = s3cmci_prepare_pio(host, cmd->data);
if (res) {
+ dbg(host, dbg_err, "data prepare error %d\n", res);
cmd->error = res;
cmd->data->error = res;
enable_irq(host->irq);
}
+static int s3cmci_card_present(struct s3cmci_host *host)
+{
+ struct s3c24xx_mci_pdata *pdata = host->pdata;
+ int ret;
+
+ if (pdata->gpio_detect == 0)
+ return -ENOSYS;
+
+ ret = s3c2410_gpio_getpin(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);
host->cmd_is_stop = 0;
host->mrq = mrq;
- s3cmci_send_request(mmc);
+ if (s3cmci_card_present(host) == 0) {
+ dbg(host, dbg_err, "%s: no medium present\n", __func__);
+ host->mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ } else
+ s3cmci_send_request(mmc);
}
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
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;
if (host->is2440)
mci_con |= S3C2440_SDICON_SDRESET;
+ if (host->pdata->set_power)
+ host->pdata->set_power(ios->power_mode, ios->vdd);
+
break;
}
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->gpio_wprotect == 0)
+ return 0;
+
+ ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
+
+ if (pdata->wprotect_invert)
+ ret = !ret;
+
+ return ret;
+}
+
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 = {
+ /* This is currently here to avoid a number of if (host->pdata)
+ * checks. Any zero fields to ensure reaonable defaults are picked. */
};
static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
host->pdev = pdev;
host->is2440 = is2440;
+ 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);
host->pio_active = XFER_NONE;
host->dma = S3CMCI_DMA;
- host->irq_cd = IRQ_EINT2;
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
disable_irq(host->irq);
- s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
- set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+ host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
- 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->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_irq;
+ }
+ } else {
+ dev_warn(&pdev->dev, "host detect has no irq available\n");
+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect,
+ S3C2410_GPIO_INPUT);
}
- if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+ 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) < 0) {
dev_err(&pdev->dev, "unable to get DMA channel.\n");
ret = -EBUSY;
goto probe_free_irq_cd;
host->clk_rate = clk_get_rate(host->clk);
mmc->ops = &s3cmci_ops;
- mmc->ocr_avail = MMC_VDD_32_33;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
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;
+ if (host->pdata->ocr_avail)
+ mmc->ocr_avail = host->pdata->ocr_avail;
+
mmc->max_blk_count = 4095;
mmc->max_blk_size = 4095;
mmc->max_req_size = 4095 * 512;
clk_put(host->clk);
probe_free_irq_cd:
- free_irq(host->irq_cd, host);
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
probe_free_irq:
free_irq(host->irq, host);
return ret;
}
+static void s3cmci_shutdown(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
+
+ mmc_remove_host(mmc);
+ clk_disable(host->clk);
+}
+
static int __devexit 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);
+ s3cmci_shutdown(pdev);
- clk_disable(host->clk);
clk_put(host->clk);
tasklet_disable(&host->pio_tasklet);
+ s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
- free_irq(host->irq_cd, host);
free_irq(host->irq, host);
iounmap(host->base);
return 0;
}
-static int __devinit s3cmci_probe_2410(struct platform_device *dev)
+static int __devinit s3cmci_2410_probe(struct platform_device *dev)
{
return s3cmci_probe(dev, 0);
}
-static int __devinit s3cmci_probe_2412(struct platform_device *dev)
+static int __devinit s3cmci_2412_probe(struct platform_device *dev)
{
return s3cmci_probe(dev, 1);
}
-static int __devinit s3cmci_probe_2440(struct platform_device *dev)
+static int __devinit s3cmci_2440_probe(struct platform_device *dev)
{
return s3cmci_probe(dev, 1);
}
#endif /* CONFIG_PM */
-static struct platform_driver s3cmci_driver_2410 = {
+static struct platform_driver s3cmci_2410_driver = {
.driver.name = "s3c2410-sdi",
.driver.owner = THIS_MODULE,
- .probe = s3cmci_probe_2410,
+ .probe = s3cmci_2410_probe,
.remove = __devexit_p(s3cmci_remove),
+ .shutdown = s3cmci_shutdown,
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
-static struct platform_driver s3cmci_driver_2412 = {
+static struct platform_driver s3cmci_2412_driver = {
.driver.name = "s3c2412-sdi",
.driver.owner = THIS_MODULE,
- .probe = s3cmci_probe_2412,
+ .probe = s3cmci_2412_probe,
.remove = __devexit_p(s3cmci_remove),
+ .shutdown = s3cmci_shutdown,
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
-static struct platform_driver s3cmci_driver_2440 = {
+static struct platform_driver s3cmci_2440_driver = {
.driver.name = "s3c2440-sdi",
.driver.owner = THIS_MODULE,
- .probe = s3cmci_probe_2440,
+ .probe = s3cmci_2440_probe,
.remove = __devexit_p(s3cmci_remove),
+ .shutdown = s3cmci_shutdown,
.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);
+ platform_driver_register(&s3cmci_2410_driver);
+ platform_driver_register(&s3cmci_2412_driver);
+ platform_driver_register(&s3cmci_2440_driver);
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);
+ platform_driver_unregister(&s3cmci_2410_driver);
+ platform_driver_unregister(&s3cmci_2412_driver);
+ platform_driver_unregister(&s3cmci_2440_driver);
}
module_init(s3cmci_init);
MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
+MODULE_ALIAS("platform:s3c2410-sdi");
+MODULE_ALIAS("platform:s3c2412-sdi");
+MODULE_ALIAS("platform:s3c2440-sdi");