Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 166658
b: refs/heads/master
c: c225889
h: refs/heads/master
v: v3
  • Loading branch information
Ben Dooks authored and Linus Torvalds committed Oct 1, 2009
1 parent c2f12d9 commit 0fcfa33
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 11 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 9bdd203b4dc82e9047486f0fed1977eef8185c6d
refs/heads/master: c225889375fea2a542f1c9dedffec4c7b8ebc9ab
161 changes: 151 additions & 10 deletions trunk/drivers/mmc/host/s3cmci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

/*
Expand Down Expand Up @@ -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) ||
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;

Expand Down
5 changes: 5 additions & 0 deletions trunk/drivers/mmc/host/s3cmci.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ struct s3cmci_host {
int dodma;
int dmatogo;

bool irq_disabled;
bool irq_enabled;
bool irq_state;
int sdio_irqen;

struct mmc_request *mrq;
int cmd_is_stop;

Expand Down

0 comments on commit 0fcfa33

Please sign in to comment.