From 6ee27681c8bbeb331bfffabfe227e0f8a4f35cb1 Mon Sep 17 00:00:00 2001 From: mokopatches Date: Wed, 19 Nov 2008 17:03:14 +0000 Subject: glamo-mmc.patch --- drivers/mfd/glamo/glamo-mci.c | 837 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 drivers/mfd/glamo/glamo-mci.c (limited to 'drivers/mfd/glamo/glamo-mci.c') diff --git a/drivers/mfd/glamo/glamo-mci.c b/drivers/mfd/glamo/glamo-mci.c new file mode 100644 index 00000000000..f559e5ebc42 --- /dev/null +++ b/drivers/mfd/glamo/glamo-mci.c @@ -0,0 +1,837 @@ +/* + * linux/drivers/mmc/host/glamo-mmc.c - Glamo MMC driver + * + * Copyright (C) 2007 OpenMoko, Inc, Andy Green + * Based on S3C MMC driver that was: + * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel + * + * 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. + */ + +#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" + +/* from glamo-core.c */ +extern struct glamo_mci_pdata glamo_mci_def_pdata; + + +#define DRIVER_NAME "glamo-mci" +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start) + 1) + +static void glamo_mci_send_request(struct mmc_host *mmc); + +unsigned char CRC7(u8 * pu8, int cnt) +{ + u8 crc = 0; + + while (cnt--) { + int n; + u8 d = *pu8++; + for (n = 0; n < 8; n++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) + crc ^= 0x09; + d <<= 1; + } + } + return (crc << 1) | 1; +} + +/* these _dly versions account for the dead time rules for reg access */ +static u16 readw_dly(u16 __iomem * pu16) +{ + glamo_reg_access_delay(); + return readw(pu16); +} + +static void writew_dly(u16 val, u16 __iomem * pu16) +{ + glamo_reg_access_delay(); + writew(val, pu16); +} + +static int get_data_buffer(struct glamo_mci_host *host, + volatile u32 *words, volatile u16 **pointer) +{ + struct scatterlist *sg; + + *words = 0; + *pointer = NULL; + + if (host->pio_active == XFER_NONE) + return -EINVAL; + + if ((!host->mrq) || (!host->mrq->data)) + return -EINVAL; + + if (host->pio_sgptr >= host->mrq->data->sg_len) { + dev_dbg(&host->pdev->dev, "no more buffers (%i/%i)\n", + host->pio_sgptr, host->mrq->data->sg_len); + return -EBUSY; + } + sg = &host->mrq->data->sg[host->pio_sgptr]; + + *words = sg->length >> 1; /* we are working with a 16-bit data bus */ + *pointer = page_address(sg_page(sg)) + sg->offset; + + BUG_ON(((long)(*pointer)) & 1); + + host->pio_sgptr++; + + /* dev_info(&host->pdev->dev, "new buffer (%i/%i)\n", + host->pio_sgptr, host->mrq->data->sg_len); */ + return 0; +} + +static void do_pio_read(struct glamo_mci_host *host) +{ + int res; + u16 __iomem *from_ptr = host->base_data + (RESSIZE(host->mem_data) / + sizeof(u16) / 2); +#ifdef DEBUG + u16 * block; +#endif + + while (1) { + res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); + if (res) { + host->pio_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; + + dev_dbg(&host->pdev->dev, "pio_read(): " + "complete (no more data).\n"); + return; + } + + dev_dbg(&host->pdev->dev, "pio_read(): host->pio_words: %d\n", + host->pio_words); + + host->pio_count += host->pio_words << 1; + +#ifdef DEBUG + block = (u16 *)host->pio_ptr; + res = host->pio_words << 1; +#endif + while (host->pio_words--) + *host->pio_ptr++ = *from_ptr++; +#ifdef DEBUG + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, + (void *)block, res, 1); +#endif + } +} + +static int do_pio_write(struct glamo_mci_host *host) +{ + int res = 0; + volatile u16 __iomem *to_ptr = host->base_data; + int err = 0; + + dev_dbg(&host->pdev->dev, "pio_write():\n"); + while (!res) { + res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); + if (res) + continue; + + dev_dbg(&host->pdev->dev, "pio_write():new source: [%i]@[%p]\n", + host->pio_words, host->pio_ptr); + + host->pio_count += host->pio_words << 1; + while (host->pio_words--) + writew(*host->pio_ptr++, to_ptr++); + } + + dev_dbg(&host->pdev->dev, "pio_write(): complete\n"); + host->pio_active = XFER_NONE; + return err; +} + +static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc) +{ + struct glamo_mci_host *host = (struct glamo_mci_host *) + desc->handler_data; + u16 status; + struct mmc_command *cmd; + unsigned long iflags; + + if (!host) + return; + if (!host->mrq) + return; + cmd = host->mrq->cmd; + if (!cmd) + return; + + spin_lock_irqsave(&host->complete_lock, iflags); + + status = readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1); + + /* ack this interrupt source */ + writew(GLAMO_IRQ_MMC, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_IRQ_CLEAR); + + 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_err(&host->pdev->dev, "Error after cmd: 0x%x\n", status); + goto done; + } + + if (host->pio_active == XFER_READ) + do_pio_read(host); + + host->mrq->data->bytes_xfered = host->pio_count; + dev_dbg(&host->pdev->dev, "status = 0x%04x count=%d\n", + status, host->pio_count); + + /* issue STOP if we have been given one to use */ + if (host->mrq->stop) { + host->cmd_is_stop = 1; + glamo_mci_send_request(host->mmc); + host->cmd_is_stop = 0; + } +done: + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); + spin_unlock_irqrestore(&host->complete_lock, iflags); +} + +static int glamo_mci_send_command(struct glamo_mci_host *host, + struct mmc_command *cmd) +{ + u8 u8a[6]; + u16 fire = 0; + + /* if we can't do it, reject as busy */ + if (!readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1) & + GLAMO_STAT1_MMC_IDLE) { + host->mrq = NULL; + cmd->error = -EBUSY; + mmc_request_done(host->mmc, host->mrq); + return -EBUSY; + } + + /* create an array in wire order for CRC computation */ + u8a[0] = 0x40 | (cmd->opcode & 0x3f); + u8a[1] = (u8)(cmd->arg >> 24); + u8a[2] = (u8)(cmd->arg >> 16); + u8a[3] = (u8)(cmd->arg >> 8); + u8a[4] = (u8)cmd->arg; + u8a[5] = CRC7(&u8a[0], 5); /* CRC7 on first 5 bytes of packet */ + + /* issue the wire-order array including CRC in register order */ + writew_dly((u8a[4] << 8) | u8a[5], host->base + GLAMO_REG_MMC_CMD_REG1); + writew_dly((u8a[2] << 8) | u8a[3], host->base + GLAMO_REG_MMC_CMD_REG2); + writew_dly((u8a[0] << 8) | u8a[1], host->base + GLAMO_REG_MMC_CMD_REG3); + + /* command index toggle */ + fire |= (host->ccnt & 1) << 12; + + /* set type of command */ + switch (mmc_cmd_type(cmd)) { + case MMC_CMD_BC: + fire |= GLAMO_FIRE_MMC_CMDT_BNR; + break; + case MMC_CMD_BCR: + fire |= GLAMO_FIRE_MMC_CMDT_BR; + break; + case MMC_CMD_AC: + fire |= GLAMO_FIRE_MMC_CMDT_AND; + break; + case MMC_CMD_ADTC: + fire |= GLAMO_FIRE_MMC_CMDT_AD; + break; + } + /* + * if it expects a response, set the type expected + * + * R1, Length : 48bit, Normal response + * R1b, Length : 48bit, same R1, but added card busy status + * R2, Length : 136bit (really 128 bits with CRC snipped) + * R3, Length : 48bit (OCR register value) + * R4, Length : 48bit, SDIO_OP_CONDITION, Reverse SDIO Card + * R5, Length : 48bit, IO_RW_DIRECTION, Reverse SDIO Card + * R6, Length : 48bit (RCA register) + * R7, Length : 48bit (interface condition, VHS(voltage supplied), + * check pattern, CRC7) + */ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R6: /* same index as R7 and R1 */ + fire |= GLAMO_FIRE_MMC_RSPT_R1; + break; + case MMC_RSP_R1B: + fire |= GLAMO_FIRE_MMC_RSPT_R1b; + break; + case MMC_RSP_R2: + fire |= GLAMO_FIRE_MMC_RSPT_R2; + break; + case MMC_RSP_R3: + fire |= GLAMO_FIRE_MMC_RSPT_R3; + break; + /* R4 and R5 supported by chip not defined in linux/mmc/core.h (sdio) */ + } + /* + * From the command index, set up the command class in the host ctrllr + * + * missing guys present on chip but couldn't figure out how to use yet: + * 0x0 "stream read" + * 0x9 "cancel running command" + */ + switch (cmd->opcode) { + case MMC_READ_SINGLE_BLOCK: + fire |= GLAMO_FIRE_MMC_CC_SBR; /* single block read */ + break; + case MMC_SWITCH: /* 64 byte payload */ + case 0x33: /* observed issued by MCI */ + case MMC_READ_MULTIPLE_BLOCK: + /* we will get an interrupt off this */ + if (!cmd->mrq->stop) + /* multiblock no stop */ + fire |= GLAMO_FIRE_MMC_CC_MBRNS; + else + /* multiblock with stop */ + fire |= GLAMO_FIRE_MMC_CC_MBRS; + break; + case MMC_WRITE_BLOCK: + fire |= GLAMO_FIRE_MMC_CC_SBW; /* single block write */ + break; + case MMC_WRITE_MULTIPLE_BLOCK: + if (cmd->mrq->stop) + /* multiblock with stop */ + fire |= GLAMO_FIRE_MMC_CC_MBWS; + else + /* multiblock NO stop-- 'RESERVED'? */ + fire |= GLAMO_FIRE_MMC_CC_MBWNS; + break; + case MMC_STOP_TRANSMISSION: + fire |= GLAMO_FIRE_MMC_CC_STOP; /* STOP */ + break; + default: + fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */ + break; + } + /* enforce timeout */ + if (cmd->data) { + if (cmd->data->timeout_clks) + writew_dly(cmd->data->timeout_clks >> 4, /* / 16 clks */ + host->base + GLAMO_REG_MMC_TIMEOUT); + else + writew_dly(0xfff, host->base + GLAMO_REG_MMC_TIMEOUT); + } else + writew(0xfff, host->base + GLAMO_REG_MMC_TIMEOUT); + + /* Generate interrupt on txfer; drive strength max */ + writew_dly((readw_dly(host->base + GLAMO_REG_MMC_BASIC) & 0xfe) | + 0x0800 | GLAMO_BASIC_MMC_NO_CLK_RD_WAIT | + GLAMO_BASIC_MMC_EN_COMPL_INT | + GLAMO_BASIC_MMC_EN_DR_STR0 | + GLAMO_BASIC_MMC_EN_DR_STR1, + host->base + GLAMO_REG_MMC_BASIC); + + /* send the command out on the wire */ + /* dev_info(&host->pdev->dev, "Using FIRE %04X\n", fire); */ + writew_dly(fire, host->base + GLAMO_REG_MMC_CMD_FIRE); + cmd->error = 0; + return 0; +} + +static int glamo_mci_prepare_pio(struct glamo_mci_host *host, + struct mmc_data *data) +{ + /* + * the S-Media-internal RAM offset for our MMC buffer + * Read is halfway up the buffer and write is at the start + */ + if (data->flags & MMC_DATA_READ) { + writew_dly((u16)(GLAMO_FB_SIZE + (RESSIZE(host->mem_data) / 2)), + host->base + GLAMO_REG_MMC_WDATADS1); + writew_dly((u16)((GLAMO_FB_SIZE + + (RESSIZE(host->mem_data) / 2)) >> 16), + host->base + GLAMO_REG_MMC_WDATADS2); + } else { + writew_dly((u16)GLAMO_FB_SIZE, host->base + + GLAMO_REG_MMC_RDATADS1); + writew_dly((u16)(GLAMO_FB_SIZE >> 16), host->base + + GLAMO_REG_MMC_RDATADS2); + } + + /* set up the block info */ + writew_dly(data->blksz, host->base + GLAMO_REG_MMC_DATBLKLEN); + writew_dly(data->blocks, host->base + GLAMO_REG_MMC_DATBLKCNT); + dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n", + data->blksz, data->blocks); + host->pio_sgptr = 0; + host->pio_words = 0; + host->pio_count = 0; + host->pio_active = 0; + /* if write, prep the write into the shared RAM before the command */ + if (data->flags & MMC_DATA_WRITE) { + host->pio_active = XFER_WRITE; + return do_pio_write(host); + } + host->pio_active = XFER_READ; + return 0; +} + +static void glamo_mci_send_request(struct mmc_host *mmc) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + u16 * pu16 = (u16 *)&cmd->resp[0]; + u16 * reg_resp = (u16 *)(host->base + GLAMO_REG_MMC_CMD_RSP1); + u16 status; + int n; + + host->ccnt++; + /* + * somehow 2.6.24 MCI manages to issue MMC_WRITE_BLOCK *without* the + * MMC_DATA_WRITE flag, WTF? Work around the madness. + */ + if (cmd->opcode == MMC_WRITE_BLOCK) + if (mrq->data) + mrq->data->flags |= MMC_DATA_WRITE; + + /* this guy has data to read/write? */ + if ((!host->cmd_is_stop) && cmd->data) { + int res; + host->dcnt++; + res = glamo_mci_prepare_pio(host, cmd->data); + if (res) { + cmd->error = -EIO; + cmd->data->error = -EIO; + mmc_request_done(mmc, mrq); + return; + } + } + + dev_dbg(&host->pdev->dev,"cmd 0x%x, " + "arg 0x%x data=%p mrq->stop=%p flags 0x%x\n", + cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop, + cmd->flags); + + if (glamo_mci_send_command(host, cmd)) + return; + /* + * we must spin until response is ready or timed out + * -- we don't get interrupts unless there is a bulk rx + */ + do + status = readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1); + while ((((status >> 15) & 1) != (host->ccnt & 1)) || + (!(status & (GLAMO_STAT1_MMC_RB_RRDY | + GLAMO_STAT1_MMC_RTOUT | + GLAMO_STAT1_MMC_DTOUT | + GLAMO_STAT1_MMC_BWERR | + GLAMO_STAT1_MMC_BRERR)))); + + 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 (host->cmd_is_stop) + return; + + if (cmd->error) { + dev_err(&host->pdev->dev, "Error after cmd: 0x%x\n", status); + goto done; + } + /* + * mangle the response registers in two different exciting + * undocumented ways discovered by trial and error + */ + if (mmc_resp_type(cmd) == MMC_RSP_R2) + /* grab the response */ + for (n = 0; n < 8; n++) /* super mangle power 1 */ + pu16[n ^ 6] = readw_dly(®_resp[n]); + else + for (n = 0; n < 3; n++) /* super mangle power 2 */ + pu16[n] = (readw_dly(®_resp[n]) >> 8) | + (readw_dly(®_resp[n + 1]) << 8); + /* + * if we don't have bulk data to take care of, we're done + */ + if (!cmd->data) + goto done; + if (!(cmd->data->flags & (MMC_DATA_READ | MMC_DATA_WRITE))) + 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, + * exit without mmc_request_done() as the payload interrupt + * 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 (!glamo_mci_def_pdata.pglamo->irq_works) { + /* we have faith we will get an "interrupt"... */ + while (!(readw_dly(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC)) + ; + /* yay we are an interrupt controller! -- call the ISR */ + glamo_mci_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC), + irq_desc + IRQ_GLAMO(GLAMO_IRQIDX_MMC)); + } + return; + +done: + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); +} + +static void glamo_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + + host->cmd_is_stop = 0; + host->mrq = mrq; + glamo_mci_send_request(mmc); +} + +static void glamo_mci_reset(struct glamo_mci_host *host) +{ + /* reset MMC controller */ + writew_dly(GLAMO_CLOCK_MMC_RESET | GLAMO_CLOCK_MMC_DG_TCLK | + GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK | + GLAMO_CLOCK_MMC_EN_M9CLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC); + msleep(1); + /* and disable reset */ + writew_dly(GLAMO_CLOCK_MMC_DG_TCLK | + GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK | + GLAMO_CLOCK_MMC_EN_M9CLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC); +} + +static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + int mci_psc = 0; + int n = 0; + + /* Set power */ + switch(ios->power_mode) { + case MMC_POWER_ON: + case MMC_POWER_UP: + if (host->power_mode_current != MMC_POWER_OFF) + break; + if (host->vdd_current != ios->vdd) { + host->pdata->glamo_set_mci_power(ios->power_mode, + ios->vdd); + host->vdd_current = ios->vdd; + } + glamo_engine_enable(glamo_mci_def_pdata.pglamo, + GLAMO_ENGINE_MMC); + glamo_mci_reset(host); + break; + + case MMC_POWER_OFF: + default: + if (host->power_mode_current == MMC_POWER_OFF) + break; + glamo_engine_disable(glamo_mci_def_pdata.pglamo, + GLAMO_ENGINE_MMC); + host->pdata->glamo_set_mci_power(MMC_POWER_OFF, 0); + host->vdd_current = -1; + break; + } + host->power_mode_current = ios->power_mode; + + /* Set clock */ +/* if (ios->clock) { */ + for (mci_psc = 0; mci_psc < 256; mci_psc++) { + host->real_rate = host->clk_rate / (mci_psc + 1); + if (host->real_rate <= ios->clock) + break; + } + if (mci_psc > 255) + mci_psc = 255; + host->clk_div = mci_psc; + /* set the nearest prescaler factor + * + * register shared with SCLK divisor -- no chance of race because + * we don't use sensor interface + */ + writew_dly((readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN8) & 0xff00) | host->clk_div, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8); + /* enable clock to divider input */ + writew_dly(readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1); +#if 0 + } else { /* stop clock */ + host->real_rate = 0; + /* remove clock from divider input */ + writew(readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK), + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1); + } +#endif + 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",ios->vdd, + host->real_rate / 1000, mci_psc, + ios->clock / 1000, ios->bus_width); + } else + dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n"); + + /* set bus width */ + host->bus_width = ios->bus_width; + if (host->bus_width == MMC_BUS_WIDTH_4) + n = GLAMO_BASIC_MMC_EN_4BIT_DATA; + writew_dly((readw_dly(host->base + GLAMO_REG_MMC_BASIC) & + (~GLAMO_BASIC_MMC_EN_4BIT_DATA)) | n, + host->base + GLAMO_REG_MMC_BASIC); +} + + +/* + * no physical write protect supported by us + */ +static int glamo_mci_get_ro(struct mmc_host *mmc) +{ + return 0; +} + +static struct mmc_host_ops glamo_mci_ops = { + .request = glamo_mci_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; + int ret; + + dev_info(&pdev->dev, "glamo_mci driver (C)2007 OpenMoko, Inc\n"); + + mmc = mmc_alloc_host(sizeof(struct glamo_mci_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto probe_out; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + host->pdata = &glamo_mci_def_pdata; + host->power_mode_current = MMC_POWER_OFF; + + host->complete_what = COMPLETION_NONE; + host->pio_active = XFER_NONE; + + spin_lock_init(&host->complete_lock); + + 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) { + dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); + ret = -EINVAL; + goto probe_free_mem_region; + } + + /* set the handler for our bit of the shared chip irq register */ + set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_MMC), glamo_mci_irq); + /* stash host as our handler's private data */ + set_irq_data(IRQ_GLAMO(GLAMO_IRQIDX_MMC), host); + + /* Get ahold of our data buffer we use for data in and out on MMC */ + host->mem_data = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!host->mem_data) { + dev_err(&pdev->dev, + "failed to get io memory region resource.\n"); + ret = -ENOENT; + goto probe_iounmap; + } + + host->mem_data = request_mem_region(host->mem_data->start, + RESSIZE(host->mem_data), pdev->name); + + if (!host->mem_data) { + dev_err(&pdev->dev, "failed to request io memory region.\n"); + ret = -ENOENT; + goto probe_iounmap; + } + host->base_data = ioremap(host->mem_data->start, + RESSIZE(host->mem_data)); + host->data_max_size = RESSIZE(host->mem_data); + + if (host->base_data == 0) { + dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); + ret = -EINVAL; + goto probe_free_mem_region_data; + } + + host->vdd_current = 0; + host->clk_rate = 50000000; /* really it's 49152000 */ + host->clk_div = 16; + + /* explain our host controller capabilities */ + mmc->ops = &glamo_mci_ops; + mmc->ocr_avail = host->pdata->ocr_avail; + mmc->caps = MMC_CAP_4_BIT_DATA | + MMC_CAP_MULTIWRITE | + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_SD_HIGHSPEED; + mmc->f_min = host->clk_rate / 256; + /* + * held at /4 due to concerns of 100R recommended series resistor + * allows 16MHz @ 4-bit --> 8MBytes/sec raw + */ + mmc->f_max = host->clk_rate / 3; + + mmc->max_blk_count = (1 << 16) - 1; /* GLAMO_REG_MMC_RB_BLKCNT */ + mmc->max_blk_size = (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */ + mmc->max_req_size = RESSIZE(host->mem_data) / 2; + mmc->max_seg_size = mmc->max_req_size; + mmc->max_phys_segs = 1; /* hw doesn't talk about segs??? */ + mmc->max_hw_segs = 1; + + dev_info(&host->pdev->dev, "probe: mapped mci_base:%p irq:%u.\n", + host->base, host->irq); + + if ((ret = mmc_add_host(mmc))) { + dev_err(&pdev->dev, "failed to add mmc host.\n"); + goto probe_free_mem_region_data; + } + + platform_set_drvdata(pdev, mmc); + + dev_info(&pdev->dev,"initialisation done.\n"); + return 0; + + probe_free_mem_region_data: + release_mem_region(host->mem_data->start, RESSIZE(host->mem_data)); + + 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 glamo_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct glamo_mci_host *host = mmc_priv(mmc); + + mmc_remove_host(mmc); + /* stop using our handler, revert it to default */ + set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_MMC), handle_level_irq); + iounmap(host->base); + iounmap(host->base_data); + release_mem_region(host->mem->start, RESSIZE(host->mem)); + release_mem_region(host->mem_data->start, RESSIZE(host->mem_data)); + mmc_free_host(mmc); + + glamo_engine_disable(glamo_mci_def_pdata.pglamo, GLAMO_ENGINE_MMC); + return 0; +} + + +#ifdef CONFIG_PM + +static int glamo_mci_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 glamo_mci_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + + return mmc_resume_host(mmc); +} + +#else /* CONFIG_PM */ +#define glamo_mci_suspend NULL +#define glamo_mci_resume 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, +}; + +static int __init glamo_mci_init(void) +{ + platform_driver_register(&glamo_mci_driver); + return 0; +} + +static void __exit glamo_mci_exit(void) +{ + platform_driver_unregister(&glamo_mci_driver); +} + +module_init(glamo_mci_init); +module_exit(glamo_mci_exit); + +MODULE_DESCRIPTION("Glamo MMC/SD Card Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andy Green "); -- cgit v1.2.3