|
@@ -190,7 +190,33 @@ static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
|
|
|
|
|
|
static inline void clear_imask(struct s3cmci_host *host)
|
|
|
{
|
|
|
- writel(0, host->base + host->sdiimsk);
|
|
|
+ u32 mask = readl(host->base + host->sdiimsk);
|
|
|
+
|
|
|
+ /* preserve the SDIO IRQ mask state */
|
|
|
+ mask &= S3C2410_SDIIMSK_SDIOIRQ;
|
|
|
+ writel(mask, host->base + host->sdiimsk);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * s3cmci_check_sdio_irq - test whether the SDIO IRQ is being signalled
|
|
|
+ * @host: The host to check.
|
|
|
+ *
|
|
|
+ * Test to see if the SDIO interrupt is being signalled in case the
|
|
|
+ * controller has failed to re-detect a card interrupt. Read GPE8 and
|
|
|
+ * see if it is low and if so, signal a SDIO interrupt.
|
|
|
+ *
|
|
|
+ * This is currently called if a request is finished (we assume that the
|
|
|
+ * bus is now idle) and when the SDIO IRQ is enabled in case the IRQ is
|
|
|
+ * already being indicated.
|
|
|
+*/
|
|
|
+static void s3cmci_check_sdio_irq(struct s3cmci_host *host)
|
|
|
+{
|
|
|
+ if (host->sdio_irqen) {
|
|
|
+ if (gpio_get_value(S3C2410_GPE(8)) == 0) {
|
|
|
+ printk(KERN_DEBUG "%s: signalling irq\n", __func__);
|
|
|
+ mmc_signal_sdio_irq(host->mmc);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static inline int get_data_buffer(struct s3cmci_host *host,
|
|
@@ -238,6 +264,64 @@ static inline u32 fifo_free(struct s3cmci_host *host)
|
|
|
return 63 - fifostat;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * s3cmci_enable_irq - enable IRQ, after having disabled it.
|
|
|
+ * @host: The device state.
|
|
|
+ * @more: True if more IRQs are expected from transfer.
|
|
|
+ *
|
|
|
+ * Enable the main IRQ if needed after it has been disabled.
|
|
|
+ *
|
|
|
+ * The IRQ can be one of the following states:
|
|
|
+ * - disabled during IDLE
|
|
|
+ * - disabled whilst processing data
|
|
|
+ * - enabled during transfer
|
|
|
+ * - enabled whilst awaiting SDIO interrupt detection
|
|
|
+ */
|
|
|
+static void s3cmci_enable_irq(struct s3cmci_host *host, bool more)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ bool enable = false;
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ host->irq_enabled = more;
|
|
|
+ host->irq_disabled = false;
|
|
|
+
|
|
|
+ enable = more | host->sdio_irqen;
|
|
|
+
|
|
|
+ if (host->irq_state != enable) {
|
|
|
+ host->irq_state = enable;
|
|
|
+
|
|
|
+ if (enable)
|
|
|
+ enable_irq(host->irq);
|
|
|
+ else
|
|
|
+ disable_irq(host->irq);
|
|
|
+ }
|
|
|
+
|
|
|
+ local_irq_restore(flags);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ */
|
|
|
+static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ //printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer);
|
|
|
+
|
|
|
+ host->irq_disabled = transfer;
|
|
|
+
|
|
|
+ if (transfer && host->irq_state) {
|
|
|
+ host->irq_state = false;
|
|
|
+ disable_irq(host->irq);
|
|
|
+ }
|
|
|
+
|
|
|
+ local_irq_restore(flags);
|
|
|
+}
|
|
|
+
|
|
|
static void do_pio_read(struct s3cmci_host *host)
|
|
|
{
|
|
|
int res;
|
|
@@ -374,8 +458,7 @@ static void pio_tasklet(unsigned long data)
|
|
|
{
|
|
|
struct s3cmci_host *host = (struct s3cmci_host *) data;
|
|
|
|
|
|
-
|
|
|
- disable_irq(host->irq);
|
|
|
+ s3cmci_disable_irq(host, true);
|
|
|
|
|
|
if (host->pio_active == XFER_WRITE)
|
|
|
do_pio_write(host);
|
|
@@ -395,9 +478,10 @@ static void pio_tasklet(unsigned long data)
|
|
|
host->mrq->data->error = -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ s3cmci_enable_irq(host, false);
|
|
|
finalize_request(host);
|
|
|
} else
|
|
|
- enable_irq(host->irq);
|
|
|
+ s3cmci_enable_irq(host, true);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -432,17 +516,27 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
|
|
|
struct s3cmci_host *host = dev_id;
|
|
|
struct mmc_command *cmd;
|
|
|
u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
|
|
|
- u32 mci_cclear, mci_dclear;
|
|
|
+ u32 mci_cclear = 0, mci_dclear;
|
|
|
unsigned long iflags;
|
|
|
|
|
|
+ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
|
|
|
+ mci_imsk = readl(host->base + host->sdiimsk);
|
|
|
+
|
|
|
+ if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {
|
|
|
+ if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
|
|
|
+ mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
|
|
|
+ writel(mci_dclear, host->base + S3C2410_SDIDSTA);
|
|
|
+
|
|
|
+ mmc_signal_sdio_irq(host->mmc);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
spin_lock_irqsave(&host->complete_lock, iflags);
|
|
|
|
|
|
mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
|
|
|
- mci_dsta = readl(host->base + S3C2410_SDIDSTA);
|
|
|
mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
|
|
|
mci_fsta = readl(host->base + S3C2410_SDIFSTA);
|
|
|
- mci_imsk = readl(host->base + host->sdiimsk);
|
|
|
- mci_cclear = 0;
|
|
|
mci_dclear = 0;
|
|
|
|
|
|
if ((host->complete_what == COMPLETION_NONE) ||
|
|
@@ -776,6 +870,8 @@ static void finalize_request(struct s3cmci_host *host)
|
|
|
request_done:
|
|
|
host->complete_what = COMPLETION_NONE;
|
|
|
host->mrq = NULL;
|
|
|
+
|
|
|
+ s3cmci_check_sdio_irq(host);
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
|
}
|
|
|
|
|
@@ -1037,7 +1133,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
|
|
|
s3cmci_send_command(host, cmd);
|
|
|
|
|
|
/* Enable Interrupt */
|
|
|
- enable_irq(host->irq);
|
|
|
+ s3cmci_enable_irq(host, true);
|
|
|
}
|
|
|
|
|
|
static int s3cmci_card_present(struct mmc_host *mmc)
|
|
@@ -1178,11 +1274,52 @@ static int s3cmci_get_ro(struct mmc_host *mmc)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
|
+{
|
|
|
+ struct s3cmci_host *host = mmc_priv(mmc);
|
|
|
+ unsigned long flags;
|
|
|
+ u32 con;
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ con = readl(host->base + S3C2410_SDICON);
|
|
|
+ host->sdio_irqen = enable;
|
|
|
+
|
|
|
+ if (enable == host->sdio_irqen)
|
|
|
+ goto same_state;
|
|
|
+
|
|
|
+ if (enable) {
|
|
|
+ con |= S3C2410_SDICON_SDIOIRQ;
|
|
|
+ enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);
|
|
|
+
|
|
|
+ if (!host->irq_state && !host->irq_disabled) {
|
|
|
+ host->irq_state = true;
|
|
|
+ enable_irq(host->irq);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);
|
|
|
+ con &= ~S3C2410_SDICON_SDIOIRQ;
|
|
|
+
|
|
|
+ if (!host->irq_enabled && host->irq_state) {
|
|
|
+ disable_irq_nosync(host->irq);
|
|
|
+ host->irq_state = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(con, host->base + S3C2410_SDICON);
|
|
|
+
|
|
|
+ same_state:
|
|
|
+ local_irq_restore(flags);
|
|
|
+
|
|
|
+ s3cmci_check_sdio_irq(host);
|
|
|
+}
|
|
|
+
|
|
|
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,
|
|
|
+ .enable_sdio_irq = s3cmci_enable_sdio_irq,
|
|
|
};
|
|
|
|
|
|
static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
|
|
@@ -1257,6 +1394,9 @@ static int s3cmci_state_show(struct seq_file *seq, void *v)
|
|
|
seq_printf(seq, "Prescale = %d\n", host->prescaler);
|
|
|
seq_printf(seq, "is2440 = %d\n", host->is2440);
|
|
|
seq_printf(seq, "IRQ = %d\n", host->irq);
|
|
|
+ seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled);
|
|
|
+ seq_printf(seq, "IRQ disabled = %d\n", host->irq_disabled);
|
|
|
+ seq_printf(seq, "IRQ state = %d\n", host->irq_state);
|
|
|
seq_printf(seq, "CD IRQ = %d\n", host->irq_cd);
|
|
|
seq_printf(seq, "Do DMA = %d\n", host->dodma);
|
|
|
seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk);
|
|
@@ -1468,6 +1608,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
|
|
|
* ensure we don't lock the system with un-serviceable requests. */
|
|
|
|
|
|
disable_irq(host->irq);
|
|
|
+ host->irq_state = false;
|
|
|
|
|
|
if (host->pdata->gpio_detect) {
|
|
|
ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");
|
|
@@ -1526,7 +1667,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
|
|
|
|
|
|
mmc->ops = &s3cmci_ops;
|
|
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
- mmc->caps = MMC_CAP_4_BIT_DATA;
|
|
|
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
|
|
mmc->f_min = host->clk_rate / (host->clk_div * 256);
|
|
|
mmc->f_max = host->clk_rate / host->clk_div;
|
|
|
|