X-Git-Url: http://git.rohieb.name/openwrt.git/blobdiff_plain/f35d974a507da20526ff3af8a9bcf5362f64e428..a0d848ed9dde978ad40ef8b752ff2d651c00091b:/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c?ds=sidebyside diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c index c9cb6ca6c..b728c8745 100644 --- a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c @@ -11,8 +11,6 @@ */ #include -#include -#include #include #include #include @@ -20,21 +18,48 @@ #include #include #include -#include #include #include +#include +#include +#include +#include -#include -#include -#include - -#include "glamo-mci.h" #include "glamo-core.h" #include "glamo-regs.h" #define DRIVER_NAME "glamo-mci" -static void glamo_mci_send_request(struct mmc_host *mmc); +struct glamo_mci_host { + struct glamo_mmc_platform_data *pdata; + struct platform_device *pdev; + struct glamo_core *core; + struct mmc_host *mmc; + struct resource *mmio_mem; + struct resource *data_mem; + void __iomem *mmio_base; + u16 __iomem *data_base; + + struct regulator *regulator; + struct mmc_request *mrq; + + unsigned int clk_rate; + + unsigned short vdd; + char power_mode; + + unsigned char request_counter; + + struct timer_list disable_timer; + + struct work_struct irq_work; + struct work_struct read_work; + + unsigned clk_enabled : 1; + +}; + +static void glamo_mci_send_request(struct mmc_host *mmc, struct mmc_request* mrq); static void glamo_mci_send_command(struct glamo_mci_host *host, struct mmc_command *cmd); @@ -51,7 +76,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host, * for example */ -static int sd_max_clk = 50000000 / 3; +static int sd_max_clk = 21000000; module_param(sd_max_clk, int, 0644); /* @@ -88,35 +113,6 @@ static int sd_post_power_clock = 1000000; module_param(sd_post_power_clock, int, 0644); -/* - * SD Signal drive strength - * - * you can override this on kernel commandline using - * - * glamo_mci.sd_drive=0 - * - * for example - */ - -static int sd_drive; -module_param(sd_drive, int, 0644); - -/* - * SD allow SD clock to run while idle - * - * you can override this on kernel commandline using - * - * glamo_mci.sd_idleclk=0 - * - * for example - */ - -static int sd_idleclk = 0; /* disallow idle clock by default */ -module_param(sd_idleclk, int, 0644); - -/* used to stash real idleclk state in suspend: we force it to run in there */ -static int suspend_sd_idleclk; - static inline void glamo_reg_write(struct glamo_mci_host *glamo, u_int16_t reg, u_int16_t val) { @@ -143,11 +139,32 @@ static void glamo_reg_set_bit_mask(struct glamo_mci_host *glamo, glamo_reg_write(glamo, reg, tmp); } -static void do_pio_read(struct glamo_mci_host *host) +static void glamo_mci_clock_disable(struct glamo_mci_host *host) { + if (host->clk_enabled) { + glamo_engine_div_disable(host->core, GLAMO_ENGINE_MMC); + host->clk_enabled = 0; + } +} + +static void glamo_mci_clock_enable(struct glamo_mci_host *host) { + del_timer_sync(&host->disable_timer); + + if (!host->clk_enabled) { + glamo_engine_div_enable(host->core, GLAMO_ENGINE_MMC); + host->clk_enabled = 1; + } +} + +static void glamo_mci_disable_timer(unsigned long data) { + struct glamo_mci_host *host = (struct glamo_mci_host *)data; + glamo_mci_clock_disable(host); +} + + +static void do_pio_read(struct glamo_mci_host *host, struct mmc_data *data) { struct scatterlist *sg; u16 __iomem *from_ptr = host->data_base; - struct mmc_data *data = host->mrq->data; void *sg_pointer; dev_dbg(&host->pdev->dev, "pio_read():\n"); @@ -162,14 +179,13 @@ static void do_pio_read(struct glamo_mci_host *host) } dev_dbg(&host->pdev->dev, "pio_read(): " - "complete (no more data).\n"); + "complete (no more data).\n"); } -static void do_pio_write(struct glamo_mci_host *host) +static void do_pio_write(struct glamo_mci_host *host, struct mmc_data *data) { struct scatterlist *sg; u16 __iomem *to_ptr = host->data_base; - struct mmc_data *data = host->mrq->data; void *sg_pointer; dev_dbg(&host->pdev->dev, "pio_write():\n"); @@ -185,81 +201,59 @@ static void do_pio_write(struct glamo_mci_host *host) dev_dbg(&host->pdev->dev, "pio_write(): complete\n"); } -static void glamo_mci_fix_card_div(struct glamo_mci_host *host, int div) -{ - unsigned long flags; - - spin_lock_irqsave(&host->pdata->pglamo->lock, flags); - - if (div < 0) { - /* stop clock - remove clock from divider input */ - writew(readw(host->pdata->pglamo->base + - GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK), - host->pdata->pglamo->base + GLAMO_REG_CLOCK_GEN5_1); - } else { - - if (host->force_slow_during_powerup) - div = host->clk_rate / sd_post_power_clock; - else if (host->pdata->glamo_mci_use_slow && - host->pdata->glamo_mci_use_slow()) - div = div * sd_slow_ratio; - - if (div > 255) - div = 255; - /* - * set the nearest prescaler factor - * - * register shared with SCLK divisor -- no chance of race because - * we don't use sensor interface - */ - writew((readw(host->pdata->pglamo->base + - GLAMO_REG_CLOCK_GEN8) & 0xff00) | div, - host->pdata->pglamo->base + GLAMO_REG_CLOCK_GEN8); - /* enable clock to divider input */ - writew(readw(host->pdata->pglamo->base + - GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK, - host->pdata->pglamo->base + GLAMO_REG_CLOCK_GEN5_1); - mdelay(5); - } - spin_unlock_irqrestore(&host->pdata->pglamo->lock, flags); -} - static int glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq) { - int div = 0; int real_rate = 0; if (freq) { - /* Set clock */ - for (div = 0; div < 255; div++) { - real_rate = host->clk_rate / (div + 1); - if (real_rate <= freq) - break; - } - - host->clk_div = div; - glamo_mci_fix_card_div(host, div); + glamo_mci_clock_enable(host); + real_rate = glamo_engine_reclock(host->core, GLAMO_ENGINE_MMC, freq); } else { - /* stop clock */ - host->clk_div = 0xff; - - if (!sd_idleclk && !host->force_slow_during_powerup) - /* clock off */ - glamo_mci_fix_card_div(host, -1); + glamo_mci_clock_disable(host); } - host->real_rate = real_rate; + return real_rate; } +static void glamo_mci_request_done(struct glamo_mci_host *host, struct +mmc_request *mrq) { + mod_timer(&host->disable_timer, jiffies + HZ / 16); + mmc_request_done(host->mmc, mrq); +} + static void glamo_mci_irq_worker(struct work_struct *work) { - struct glamo_mci_host *host = - container_of(work, struct glamo_mci_host, irq_work); - struct mmc_command *cmd = host->mrq->cmd; + struct glamo_mci_host *host = container_of(work, struct glamo_mci_host, + irq_work); + struct mmc_command *cmd; + uint16_t status; + if (!host->mrq || !host->mrq->cmd) + return; + cmd = host->mrq->cmd; + +#if 0 if (cmd->data->flags & MMC_DATA_READ) { - do_pio_read(host); + return; + } +#endif + + status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1); + dev_dbg(&host->pdev->dev, "status = 0x%04x\n", status); + + /* we ignore a data timeout report if we are also told the data came */ + if (status & GLAMO_STAT1_MMC_RB_DRDY) + status &= ~GLAMO_STAT1_MMC_DTOUT; + + if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT)) + cmd->error = -ETIMEDOUT; + if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) { + cmd->error = -EILSEQ; + } + if (cmd->error) { + dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status); + goto done; } /* issue STOP if we have been given one to use */ @@ -267,72 +261,80 @@ static void glamo_mci_irq_worker(struct work_struct *work) glamo_mci_send_command(host, host->mrq->stop); } - if (!sd_idleclk && !host->force_slow_during_powerup) - /* clock off */ - glamo_mci_fix_card_div(host, -1); + if (cmd->data->flags & MMC_DATA_READ) + do_pio_read(host, cmd->data); +done: host->mrq = NULL; - mmc_request_done(host->mmc, cmd->mrq); + glamo_mci_request_done(host, cmd->mrq); } -static irqreturn_t glamo_mci_irq(int irq, void *devid) +static void glamo_mci_read_worker(struct work_struct *work) { - struct glamo_mci_host *host = (struct glamo_mci_host*)devid; - u16 status; + struct glamo_mci_host *host = container_of(work, struct glamo_mci_host, + read_work); struct mmc_command *cmd; - unsigned long flags; + uint16_t status; + uint16_t blocks_ready; + size_t data_read = 0; + size_t data_ready; + struct scatterlist *sg; + u16 __iomem *from_ptr = host->data_base; + void *sg_pointer; - if (host->suspending) { /* bad news, dangerous time */ - dev_err(&host->pdev->dev, "****glamo_mci_irq before resumed\n"); - goto leave; - } - if (!host->mrq) - goto leave; cmd = host->mrq->cmd; - if (!cmd) - goto leave; + sg = cmd->data->sg; + do { + status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1); + + if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT)) + cmd->error = -ETIMEDOUT; + if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) + cmd->error = -EILSEQ; + if (cmd->error) { + dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status); + goto done; + } - spin_lock_irqsave(&host->lock, flags); + blocks_ready = glamo_reg_read(host, GLAMO_REG_MMC_RB_BLKCNT); + data_ready = blocks_ready * cmd->data->blksz; - status = readw(host->mmio_base + GLAMO_REG_MMC_RB_STAT1); - dev_dbg(&host->pdev->dev, "status = 0x%04x\n", status); + if (data_ready == data_read) + yield(); - /* we ignore a data timeout report if we are also told the data came */ - if (status & GLAMO_STAT1_MMC_RB_DRDY) - status &= ~GLAMO_STAT1_MMC_DTOUT; + while(sg && data_read + sg->length <= data_ready) { + sg_pointer = page_address(sg_page(sg)) + sg->offset; + memcpy(sg_pointer, from_ptr, sg->length); + from_ptr += sg->length >> 1; - if (status & (GLAMO_STAT1_MMC_RTOUT | - GLAMO_STAT1_MMC_DTOUT)) - cmd->error = -ETIMEDOUT; - if (status & (GLAMO_STAT1_MMC_BWERR | - GLAMO_STAT1_MMC_BRERR)) - cmd->error = -EILSEQ; - if (cmd->error) { - dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status); - goto done; - } + data_read += sg->length; + sg = sg_next(sg); + } - /* - * disable the initial slow start after first bulk transfer - */ - if (host->force_slow_during_powerup) - host->force_slow_during_powerup--; + } while(sg); + cmd->data->bytes_xfered = data_read; - /* - * we perform the memcpy out of Glamo memory outside of IRQ context - * so we don't block other interrupts - */ - schedule_work(&host->irq_work); + do { + status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1); + } while (!(status & GLAMO_STAT1_MMC_IDLE)); - goto unlock; + if (host->mrq->stop) + glamo_mci_send_command(host, host->mrq->stop); + do { + status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1); + } while (!(status & GLAMO_STAT1_MMC_IDLE)); done: host->mrq = NULL; - mmc_request_done(host->mmc, cmd->mrq); -unlock: - spin_unlock_irqrestore(&host->lock, flags); -leave: + glamo_mci_request_done(host, cmd->mrq); +} + +static irqreturn_t glamo_mci_irq(int irq, void *devid) +{ + struct glamo_mci_host *host = (struct glamo_mci_host*)devid; + schedule_work(&host->irq_work); + return IRQ_HANDLED; } @@ -344,13 +346,12 @@ static void glamo_mci_send_command(struct glamo_mci_host *host, unsigned int timeout = 1000000; u16 * reg_resp = (u16 *)(host->mmio_base + GLAMO_REG_MMC_CMD_RSP1); u16 status; + int triggers_int = 1; /* if we can't do it, reject as busy */ - if (!readw(host->mmio_base + GLAMO_REG_MMC_RB_STAT1) & - GLAMO_STAT1_MMC_IDLE) { - host->mrq = NULL; + if (!glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1) & + GLAMO_STAT1_MMC_IDLE) { cmd->error = -EBUSY; - mmc_request_done(host->mmc, host->mrq); return; } @@ -363,9 +364,9 @@ static void glamo_mci_send_command(struct glamo_mci_host *host, u8a[5] = (crc7(0, u8a, 5) << 1) | 0x01; /* crc7 on first 5 bytes of packet */ /* issue the wire-order array including CRC in register order */ - writew((u8a[4] << 8) | u8a[5], host->mmio_base + GLAMO_REG_MMC_CMD_REG1); - writew((u8a[2] << 8) | u8a[3], host->mmio_base + GLAMO_REG_MMC_CMD_REG2); - writew((u8a[0] << 8) | u8a[1], host->mmio_base + GLAMO_REG_MMC_CMD_REG3); + glamo_reg_write(host, GLAMO_REG_MMC_CMD_REG1, ((u8a[4] << 8) | u8a[5])); + glamo_reg_write(host, GLAMO_REG_MMC_CMD_REG2, ((u8a[2] << 8) | u8a[3])); + glamo_reg_write(host, GLAMO_REG_MMC_CMD_REG3, ((u8a[0] << 8) | u8a[1])); /* command index toggle */ fire |= (host->request_counter & 1) << 12; @@ -443,27 +444,36 @@ static void glamo_mci_send_command(struct glamo_mci_host *host, /* multiblock with stop */ fire |= GLAMO_FIRE_MMC_CC_MBWS; else - /* multiblock NO stop-- 'RESERVED'? */ + /* multiblock NO stop-- 'RESERVED'? */ fire |= GLAMO_FIRE_MMC_CC_MBWNS; break; case MMC_STOP_TRANSMISSION: fire |= GLAMO_FIRE_MMC_CC_STOP; /* STOP */ + triggers_int = 0; break; default: fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */ + triggers_int = 0; break; } + + if (cmd->data) + host->mrq = cmd->mrq; + /* always largest timeout */ - writew(0xfff, host->mmio_base + GLAMO_REG_MMC_TIMEOUT); + glamo_reg_write(host, GLAMO_REG_MMC_TIMEOUT, 0xfff); /* Generate interrupt on txfer */ - glamo_reg_set_bit_mask(host, GLAMO_REG_MMC_BASIC, ~0x3e, - 0x0800 | GLAMO_BASIC_MMC_NO_CLK_RD_WAIT | - GLAMO_BASIC_MMC_EN_COMPL_INT | (sd_drive << 6)); + glamo_reg_set_bit_mask(host, GLAMO_REG_MMC_BASIC, 0xff36, + 0x0800 | + GLAMO_BASIC_MMC_NO_CLK_RD_WAIT | + GLAMO_BASIC_MMC_EN_COMPL_INT | + GLAMO_BASIC_MMC_EN_DATA_PUPS | + GLAMO_BASIC_MMC_EN_CMD_PUP); /* send the command out on the wire */ /* dev_info(&host->pdev->dev, "Using FIRE %04X\n", fire); */ - writew(fire, host->mmio_base + GLAMO_REG_MMC_CMD_FIRE); + glamo_reg_write(host, GLAMO_REG_MMC_CMD_FIRE, fire); /* we are deselecting card? because it isn't going to ack then... */ if ((cmd->opcode == 7) && (cmd->arg == 0)) @@ -473,71 +483,115 @@ static void glamo_mci_send_command(struct glamo_mci_host *host, * we must spin until response is ready or timed out * -- we don't get interrupts unless there is a bulk rx */ - udelay(5); do - status = readw(host->mmio_base + GLAMO_REG_MMC_RB_STAT1); + status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1); while (((((status >> 15) & 1) != (host->request_counter & 1)) || (!(status & (GLAMO_STAT1_MMC_RB_RRDY | - GLAMO_STAT1_MMC_RTOUT | - GLAMO_STAT1_MMC_DTOUT | - GLAMO_STAT1_MMC_BWERR | - GLAMO_STAT1_MMC_BRERR)))) && (timeout--)); + GLAMO_STAT1_MMC_RTOUT | + GLAMO_STAT1_MMC_DTOUT | + GLAMO_STAT1_MMC_BWERR | + GLAMO_STAT1_MMC_BRERR)))) && (timeout--)); if ((status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT)) || (timeout == 0)) { cmd->error = -ETIMEDOUT; - } else if (status & (GLAMO_STAT1_MMC_BWERR | - GLAMO_STAT1_MMC_BRERR)) { + } else if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) { cmd->error = -EILSEQ; } if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { cmd->resp[3] = readw(®_resp[0]) | - (readw(®_resp[1]) << 16); + (readw(®_resp[1]) << 16); cmd->resp[2] = readw(®_resp[2]) | - (readw(®_resp[3]) << 16); + (readw(®_resp[3]) << 16); cmd->resp[1] = readw(®_resp[4]) | - (readw(®_resp[5]) << 16); + (readw(®_resp[5]) << 16); cmd->resp[0] = readw(®_resp[6]) | - (readw(®_resp[7]) << 16); + (readw(®_resp[7]) << 16); } else { cmd->resp[0] = (readw(®_resp[0]) >> 8) | - (readw(®_resp[1]) << 8) | - ((readw(®_resp[2])) << 24); + (readw(®_resp[1]) << 8) | + ((readw(®_resp[2])) << 24); } } + +#if 0 + /* We'll only get an interrupt when all data has been transfered. + By starting to copy data when it's avaiable we can increase throughput by + up to 30%. */ + if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) + schedule_work(&host->read_work); +#endif + } static int glamo_mci_prepare_pio(struct glamo_mci_host *host, struct mmc_data *data) { /* set up the block info */ - writew(data->blksz, host->mmio_base + GLAMO_REG_MMC_DATBLKLEN); - writew(data->blocks, host->mmio_base + GLAMO_REG_MMC_DATBLKCNT); - dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n", - data->blksz, data->blocks); + glamo_reg_write(host, GLAMO_REG_MMC_DATBLKLEN, data->blksz); + glamo_reg_write(host, GLAMO_REG_MMC_DATBLKCNT, data->blocks); + data->bytes_xfered = 0; /* if write, prep the write into the shared RAM before the command */ if (data->flags & MMC_DATA_WRITE) { - do_pio_write(host); + do_pio_write(host, data); + } + + dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n", + data->blksz, data->blocks); + return 0; +} + +static int glamo_mci_irq_poll(struct glamo_mci_host *host, + struct mmc_command *cmd) +{ + int timeout = 1000000; + /* + * if the glamo INT# line isn't wired (*cough* it can happen) + * I'm afraid we have to spin on the IRQ status bit and "be + * our own INT# line" + */ + /* + * we have faith we will get an "interrupt"... + * but something insane like suspend problems can mean + * we spin here forever, so we timeout after a LONG time + */ + while ((!(readw(host->core->base + + GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC)) && + (timeout--)); + + if (timeout < 0) { + if (cmd->data->error) + cmd->data->error = -ETIMEDOUT; + dev_err(&host->pdev->dev, "Payload timeout\n"); + return -ETIMEDOUT; } + /* ack this interrupt source */ + writew(GLAMO_IRQ_MMC, host->core->base + + GLAMO_REG_IRQ_CLEAR); + + /* yay we are an interrupt controller! -- call the ISR + * it will stop clock to card + */ + glamo_mci_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC), host); + return 0; } -static void glamo_mci_send_request(struct mmc_host *mmc) +static void glamo_mci_send_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct glamo_mci_host *host = mmc_priv(mmc); - struct mmc_request *mrq = host->mrq; struct mmc_command *cmd = mrq->cmd; - int timeout = 1000000; - host->request_counter++; - /* this guy has data to read/write? */ + glamo_mci_clock_enable(host); + host->request_counter++; if (cmd->data) { if(glamo_mci_prepare_pio(host, cmd->data)) { + cmd->error = -EIO; cmd->data->error = -EIO; goto done; } @@ -548,11 +602,6 @@ static void glamo_mci_send_request(struct mmc_host *mmc) cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop, cmd->flags); - /* resume requested clock rate - * scale it down by sd_slow_ratio if platform requests it - */ - glamo_mci_fix_card_div(host, host->clk_div); - glamo_mci_send_command(host, cmd); /* @@ -561,6 +610,12 @@ static void glamo_mci_send_request(struct mmc_host *mmc) if (!cmd->data || cmd->error) goto done; + + if (!host->core->irq_works) { + if (glamo_mci_irq_poll(host, mrq->cmd)) + goto done; + } + /* * Otherwise can can use the interrupt as async completion -- * if there is read data coming, or we wait for write data to complete, @@ -568,135 +623,82 @@ static void glamo_mci_send_request(struct mmc_host *mmc) * will service it */ dev_dbg(&host->pdev->dev, "Waiting for payload data\n"); - /* - * if the glamo INT# line isn't wired (*cough* it can happen) - * I'm afraid we have to spin on the IRQ status bit and "be - * our own INT# line" - */ - if (!host->pdata->pglamo->irq_works) { - /* - * we have faith we will get an "interrupt"... - * but something insane like suspend problems can mean - * we spin here forever, so we timeout after a LONG time - */ - while ((!(readw(host->pdata->pglamo->base + - GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC)) && - (timeout--)); - - if (timeout < 0) { - if (cmd->data->error) - cmd->data->error = -ETIMEDOUT; - dev_err(&host->pdev->dev, "Payload timeout\n"); - goto bail; - } - /* ack this interrupt source */ - writew(GLAMO_IRQ_MMC, host->pdata->pglamo->base + - GLAMO_REG_IRQ_CLEAR); - - /* yay we are an interrupt controller! -- call the ISR - * it will stop clock to card - */ - glamo_mci_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC), host); - } - return; + return; done: - host->mrq = NULL; - mmc_request_done(host->mmc, cmd->mrq); -bail: - if (!sd_idleclk && !host->force_slow_during_powerup) - /* stop the clock to card */ - glamo_mci_fix_card_div(host, -1); -} - -static void glamo_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct glamo_mci_host *host = mmc_priv(mmc); - - host->mrq = mrq; - glamo_mci_send_request(mmc); + glamo_mci_request_done(host, mrq); } -static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct glamo_mci_host *host = mmc_priv(mmc); - int bus_width = 0; - int powering = 0; +static void glamo_mci_set_power_mode(struct glamo_mci_host *host, + unsigned char power_mode) { + int ret; - if (host->suspending) { - dev_err(&host->pdev->dev, "IGNORING glamo_mci_set_ios while " - "suspended\n"); + if (power_mode == host->power_mode) return; - } - /* Set power */ - switch(ios->power_mode) { + switch(power_mode) { case MMC_POWER_UP: - mmc_regulator_set_ocr(host->regulator, ios->vdd); - host->vdd_current = ios->vdd; + if (host->power_mode == MMC_POWER_OFF) { + ret = regulator_enable(host->regulator); + if (ret) + dev_err(&host->pdev->dev, "Failed to enable regulator: %d\n", ret); + } break; case MMC_POWER_ON: - /* - * we should use very slow clock until first bulk - * transfer completes OK - */ - host->force_slow_during_powerup = 1; - - if (host->vdd_current != ios->vdd) { - mmc_regulator_set_ocr(host->regulator, ios->vdd); - host->vdd_current = ios->vdd; - } - if (host->power_mode_current == MMC_POWER_OFF) { - glamo_engine_enable(host->pdata->pglamo, - GLAMO_ENGINE_MMC); - powering = 1; - } break; - case MMC_POWER_OFF: default: - if (host->power_mode_current == MMC_POWER_OFF) - break; - /* never want clocking with dead card */ - glamo_mci_fix_card_div(host, -1); - - glamo_engine_disable(host->pdata->pglamo, + glamo_engine_disable(host->core, GLAMO_ENGINE_MMC); - mmc_regulator_set_ocr(host->regulator, 0); - host->vdd_current = -1; + ret = regulator_disable(host->regulator); + if (ret) + dev_warn(&host->pdev->dev, "Failed to disable regulator: %d\n", ret); break; } - host->power_mode_current = ios->power_mode; + host->power_mode = power_mode; +} - glamo_mci_set_card_clock(host, ios->clock); +static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + int bus_width = 0; + int rate; + int sd_drive; + int ret; - /* after power-up, we are meant to give it >= 74 clocks so it can - * initialize itself. Doubt any modern cards need it but anyway... - */ - if (powering) - mdelay(1); + /* Set power */ + glamo_mci_set_power_mode(host, ios->power_mode); - if (!sd_idleclk && !host->force_slow_during_powerup) - /* stop the clock to card, because we are idle until transfer */ - glamo_mci_fix_card_div(host, -1); + if (host->vdd != ios->vdd) { + ret = mmc_regulator_set_ocr(host->regulator, ios->vdd); + if (ret) + dev_err(&host->pdev->dev, "Failed to set regulator voltage: %d\n", ret); + else + host->vdd = ios->vdd; + } + rate = glamo_mci_set_card_clock(host, ios->clock); if ((ios->power_mode == MMC_POWER_ON) || (ios->power_mode == MMC_POWER_UP)) { dev_info(&host->pdev->dev, - "powered (vdd = %d) clk: %lukHz div=%d (req: %ukHz). " - "Bus width=%d\n",(int)ios->vdd, - host->real_rate / 1000, (int)host->clk_div, + "powered (vdd = %hu) clk: %dkHz div=%hu (req: %ukHz). " + "Bus width=%d\n", ios->vdd, + rate / 1000, 0, ios->clock / 1000, (int)ios->bus_width); - } else + } else { dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n"); + } /* set bus width */ if (ios->bus_width == MMC_BUS_WIDTH_4) bus_width = GLAMO_BASIC_MMC_EN_4BIT_DATA; + + sd_drive = (rate * 4) / host->clk_rate; + if (sd_drive > 3) + sd_drive = 3; + glamo_reg_set_bit_mask(host, GLAMO_REG_MMC_BASIC, - GLAMO_BASIC_MMC_EN_4BIT_DATA | - GLAMO_BASIC_MMC_EN_DR_STR0 | - GLAMO_BASIC_MMC_EN_DR_STR1, + GLAMO_BASIC_MMC_EN_4BIT_DATA | 0xb0, bus_width | sd_drive << 6); } @@ -710,15 +712,16 @@ static int glamo_mci_get_ro(struct mmc_host *mmc) } static struct mmc_host_ops glamo_mci_ops = { - .request = glamo_mci_request, + .request = glamo_mci_send_request, .set_ios = glamo_mci_set_ios, .get_ro = glamo_mci_get_ro, }; static int glamo_mci_probe(struct platform_device *pdev) { - struct mmc_host *mmc; - struct glamo_mci_host *host; + struct mmc_host *mmc; + struct glamo_mci_host *host; + struct glamo_core *core = dev_get_drvdata(pdev->dev.parent); int ret; dev_info(&pdev->dev, "glamo_mci driver (C)2007 Openmoko, Inc\n"); @@ -732,11 +735,14 @@ static int glamo_mci_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->pdev = pdev; - host->pdata = pdev->dev.platform_data; - host->power_mode_current = MMC_POWER_OFF; + if (core->pdata) + host->pdata = core->pdata->mmc_data; + host->power_mode = MMC_POWER_OFF; + host->clk_enabled = 0; + host->core = core; - spin_lock_init(&host->lock); INIT_WORK(&host->irq_work, glamo_mci_irq_worker); + INIT_WORK(&host->read_work, glamo_mci_read_worker); host->regulator = regulator_get(pdev->dev.parent, "SD_3V3"); if (!host->regulator) { @@ -801,15 +807,14 @@ static int glamo_mci_probe(struct platform_device *pdev) ret = request_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC), glamo_mci_irq, IRQF_SHARED, pdev->name, host); - if (ret) { + if (ret) { dev_err(&pdev->dev, "failed to register irq.\n"); goto probe_iounmap_data; } - host->vdd_current = -1; - host->clk_rate = 50000000; /* really it's 49152000 */ - host->clk_div = 16; + host->vdd = 0; + host->clk_rate = glamo_pll_rate(host->core, GLAMO_PLL1); /* explain our host controller capabilities */ mmc->ops = &glamo_mci_ops; @@ -827,33 +832,42 @@ static int glamo_mci_probe(struct platform_device *pdev) mmc->max_phys_segs = 128; mmc->max_hw_segs = 128; + if (mmc->ocr_avail < 0) { + dev_warn(&pdev->dev, "Failed to get ocr list for regulator: %d.\n", + mmc->ocr_avail); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + } + platform_set_drvdata(pdev, mmc); - glamo_engine_enable(host->pdata->pglamo, GLAMO_ENGINE_MMC); - glamo_engine_reset(host->pdata->pglamo, GLAMO_ENGINE_MMC); + glamo_engine_enable(host->core, GLAMO_ENGINE_MMC); + glamo_engine_reset(host->core, GLAMO_ENGINE_MMC); + + glamo_reg_write(host, GLAMO_REG_MMC_WDATADS1, + (u16)(host->data_mem->start)); + glamo_reg_write(host, GLAMO_REG_MMC_WDATADS2, + (u16)(host->data_mem->start >> 16)); + + glamo_reg_write(host, GLAMO_REG_MMC_RDATADS1, + (u16)(host->data_mem->start)); + glamo_reg_write(host, GLAMO_REG_MMC_RDATADS2, + (u16)(host->data_mem->start >> 16)); + + setup_timer(&host->disable_timer, glamo_mci_disable_timer, + (unsigned long)host); if ((ret = mmc_add_host(mmc))) { dev_err(&pdev->dev, "failed to add mmc host.\n"); goto probe_freeirq; } - writew((u16)(host->data_mem->start), - host->mmio_base + GLAMO_REG_MMC_WDATADS1); - writew((u16)((host->data_mem->start) >> 16), - host->mmio_base + GLAMO_REG_MMC_WDATADS2); - - writew((u16)host->data_mem->start, host->mmio_base + - GLAMO_REG_MMC_RDATADS1); - writew((u16)(host->data_mem->start >> 16), host->mmio_base + - GLAMO_REG_MMC_RDATADS2); - dev_info(&pdev->dev,"initialisation done.\n"); return 0; probe_freeirq: free_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC), host); probe_iounmap_data: - iounmap(host->data_base); + iounmap(host->data_base); probe_free_mem_region_data: release_mem_region(host->data_mem->start, resource_size(host->data_mem)); probe_iounmap_mmio: @@ -861,7 +875,7 @@ probe_iounmap_mmio: probe_free_mem_region_mmio: release_mem_region(host->mmio_mem->start, resource_size(host->mmio_mem)); probe_regulator_put: - regulator_put(host->regulator); + regulator_put(host->regulator); probe_free_host: mmc_free_host(mmc); probe_out: @@ -885,77 +899,73 @@ static int glamo_mci_remove(struct platform_device *pdev) mmc_free_host(mmc); - glamo_engine_disable(host->pdata->pglamo, GLAMO_ENGINE_MMC); + glamo_engine_disable(host->core, GLAMO_ENGINE_MMC); return 0; } #ifdef CONFIG_PM -static int glamo_mci_suspend(struct platform_device *dev, pm_message_t state) +static int glamo_mci_suspend(struct device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); - struct glamo_mci_host *host = mmc_priv(mmc); + struct mmc_host *mmc = dev_get_drvdata(dev); + struct glamo_mci_host *host = mmc_priv(mmc); int ret; cancel_work_sync(&host->irq_work); - /* - * possible workaround for SD corruption during suspend - resume - * make sure the clock was running during suspend and consequently - * resume - */ - glamo_mci_fix_card_div(host, host->clk_div); - - /* we are going to do more commands to override this in - * mmc_suspend_host(), so we need to change sd_idleclk for the - * duration as well - */ - suspend_sd_idleclk = sd_idleclk; - sd_idleclk = 1; - - ret = mmc_suspend_host(mmc, state); - - host->suspending++; + ret = mmc_suspend_host(mmc, PMSG_SUSPEND); + glamo_mci_clock_enable(host); return ret; } -int glamo_mci_resume(struct platform_device *dev) +static int glamo_mci_resume(struct device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); - struct glamo_mci_host *host = mmc_priv(mmc); + struct mmc_host *mmc = dev_get_drvdata(dev); + struct glamo_mci_host *host = mmc_priv(mmc); int ret; - sd_idleclk = 1; - - glamo_engine_enable(host->pdata->pglamo, GLAMO_ENGINE_MMC); - glamo_engine_reset(host->pdata->pglamo, GLAMO_ENGINE_MMC); + glamo_engine_enable(host->core, GLAMO_ENGINE_MMC); + glamo_engine_reset(host->core, GLAMO_ENGINE_MMC); - host->suspending--; + glamo_reg_write(host, GLAMO_REG_MMC_WDATADS1, + (u16)(host->data_mem->start)); + glamo_reg_write(host, GLAMO_REG_MMC_WDATADS2, + (u16)(host->data_mem->start >> 16)); - ret = mmc_resume_host(mmc); + glamo_reg_write(host, GLAMO_REG_MMC_RDATADS1, + (u16)(host->data_mem->start)); + glamo_reg_write(host, GLAMO_REG_MMC_RDATADS2, + (u16)(host->data_mem->start >> 16)); + mdelay(5); - /* put sd_idleclk back to pre-suspend state */ - sd_idleclk = suspend_sd_idleclk; + ret = mmc_resume_host(host->mmc); +/* glamo_mci_clock_disable(host);*/ - return ret; + return 0; } -EXPORT_SYMBOL_GPL(glamo_mci_resume); + +static struct dev_pm_ops glamo_mci_pm_ops = { + .suspend = glamo_mci_suspend, + .resume = glamo_mci_resume, +}; +#define GLAMO_MCI_PM_OPS (&glamo_mci_pm_ops) #else /* CONFIG_PM */ -#define glamo_mci_suspend NULL -#define glamo_mci_resume NULL +#define GLAMO_MCI_PM_OPS NULL #endif /* CONFIG_PM */ static struct platform_driver glamo_mci_driver = { - .driver.name = "glamo-mci", - .probe = glamo_mci_probe, - .remove = glamo_mci_remove, - .suspend = glamo_mci_suspend, - .resume = glamo_mci_resume, + .probe = glamo_mci_probe, + .remove = glamo_mci_remove, + .driver = { + .name = "glamo-mci", + .owner = THIS_MODULE, + .pm = GLAMO_MCI_PM_OPS, + }, }; static int __init glamo_mci_init(void)