/*
 *  linux/drivers/mmc/asic3_mmc.c
 *
 *  Copyright (c) 2005 SDG Systems, LLC
 *  Copyright (C) 2005 Pawel Kolodziejski
 *
 *  based on tmio_mmc.c
 *      Copyright (C) 2004 Ian Molton
 *
 * 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.
 *
 * Driver for the SD / SDIO cell found in:
 *
 * TC6393XB
 *
 * This driver draws mainly on scattered spec sheets, Reverse engineering
 * of the toshiba e800  SD driver and some parts of the 2.4 ASIC3 driver (4 bit
 * support).
 *
 * Supports MMC 1 bit transfers and SD 1 and 4 bit modes.
 *
 * TODO:
 *   Eliminate FIXMEs
 *   SDIO support
 *   Power management
 *   Handle MMC errors (at all)
 *
 */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/hardware/amba.h>
#include <asm/hardware/clock.h>
#include <asm/mach-types.h>

//#include <asm/arch/h1900-gpio.h>
#include <asm/arch/h4000-gpio.h>
#include <asm/hardware/ipaq-asic3.h>

#ifdef CONFIG_MMC_DEBUG
#define DBG(x...)       printk("#### mmc_asic3: " x)
#else
#define DBG(x...)       do { } while (0)
#endif

#define DRIVER_NAME "asic3_mmc"

struct asic3_mmc_host {
    void                    *ctl_base;
    void                    *cnf_base;
    struct mmc_command      *cmd;
    struct mmc_request      *mrq;
    struct mmc_data         *data;
    struct mmc_host         *mmc;
    int                     irq;
    unsigned short          clock_for_sd;

    /* I/O related stuff */
    struct scatterlist      *sg_ptr;
    unsigned int            sg_len;
    unsigned int            sg_off;
};

static void
mmc_finish_request(struct asic3_mmc_host *host)
{
    struct mmc_request *mrq = host->mrq;

    /* Write something to end the command */
    host->mrq  = NULL;
    host->cmd  = NULL;
    host->data = NULL;

    mmc_request_done(host->mmc, mrq);
}

/* Response types */
#define APP_CMD        0x0040
#define RESP_NONE      SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL
#define RESP_R1        SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1
#define RESP_R1B       SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B
#define RESP_R2        SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2
#define RESP_R3        SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3

#define DATA_PRESENT   SD_CTRL_COMMAND_DATA_PRESENT
#define TRANSFER_READ  SD_CTRL_COMMAND_TRANSFER_READ
#define TRANSFER_MULTI SD_CTRL_COMMAND_MULTI_BLOCK
#define SECURITY_CMD   SD_CTRL_COMMAND_SECURITY_CMD

static void
mmc_start_command(struct asic3_mmc_host *host, struct mmc_command *cmd)
{
    void *base            = host->ctl_base;
    struct mmc_data *data = host->data;
    int c                 = cmd->opcode;

    DBG("[1;33mOpcode: %d[0m, base: %p\n", cmd->opcode, base);

    if(cmd->opcode == MMC_STOP_TRANSMISSION) {
        IPAQ_ASIC3_SD_CTRL_StopInternal(base) = SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12;
        cmd->resp[0] = cmd->opcode;
        cmd->resp[1] = 0;
        cmd->resp[2] = 0;
        cmd->resp[3] = 0;
        cmd->resp[4] = 0;
        return;
    }

    switch(cmd->flags & 0x1b) {
        case MMC_RSP_NONE: c |= RESP_NONE; break;
        case MMC_RSP_R1:   c |= RESP_R1;   break;
        case MMC_RSP_R1B:  c |= RESP_R1B;  break;
        case MMC_RSP_R2:   c |= RESP_R2;   break;
        case MMC_RSP_R3:   c |= RESP_R3;   break;
        default:
            DBG("Unknown response type %d\n", cmd->flags & MMC_RSP_MASK);
            break;
    }

    host->cmd = cmd;

    if(cmd->opcode == MMC_APP_CMD) {
        c |= APP_CMD;
    }
    if (cmd->opcode == MMC_GO_IDLE_STATE) {
        c |= (3 << 8); /* This was removed from ipaq-asic3.h for some reason */
    }
    if(data) {
        c |= DATA_PRESENT;
        if(data->blocks > 1) {
            IPAQ_ASIC3_SD_CTRL_StopInternal(base) = SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12;
            c |= TRANSFER_MULTI;
        }
        if(data->flags & MMC_DATA_READ) {
            c |= TRANSFER_READ;
        }
        /* MMC_DATA_WRITE does not require a bit to be set */
    }

    /* Enable the command and data interrupts */
    IPAQ_ASIC3_SD_CTRL_IntMaskCard(host->ctl_base) = ~(
          SD_CTRL_INTMASKCARD_RESPONSE_END
        | SD_CTRL_INTMASKCARD_RW_END
        | SD_CTRL_INTMASKCARD_CARD_REMOVED_0
        | SD_CTRL_INTMASKCARD_CARD_INSERTED_0
#if 0
        | SD_CTRL_INTMASKCARD_CARD_REMOVED_3
        | SD_CTRL_INTMASKCARD_CARD_INSERTED_3
#endif
    );

    IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) = ~(
          SD_CTRL_INTMASKBUFFER_UNK7
#if 0
        | SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR
        | SD_CTRL_INTMASKBUFFER_CRC_ERROR
        | SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR
        | SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT
        | SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW
        | SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW
        | SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT
        | SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE
        | SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE
        | SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS
#endif
    );

    /* Send the command */
    IPAQ_ASIC3_SD_CTRL_Arg1(base) = cmd->arg >> 16;
    IPAQ_ASIC3_SD_CTRL_Arg0(base) = cmd->arg & 0xffff;
    IPAQ_ASIC3_SD_CTRL_Cmd(base) = c;
}

/* This chip always returns (at least?) as much data as you ask for.  I'm
 * unsure what happens if you ask for less than a block. This should be looked
 * into to ensure that a funny length read doesnt mess up the controller data
 * state machine.
 *
 * Aric: Statement above may not apply to ASIC3.
 *
 * FIXME - this chip cannot do 1 and 2 byte data requests in 4 bit mode
 *
 * Aric: Statement above may not apply to ASIC3.
 */
static void
mmc_data_irq(struct asic3_mmc_host *host)
{
    struct mmc_data *data = host->data;
    unsigned short *buf;
    int count;
    unsigned long flags;

    if(!data){
        DBG("Spurious Data IRQ\n");
        return;
    }

    local_irq_save(flags);
    buf = kmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ);
    buf += host->sg_ptr->offset/2 + host->sg_off/2;

    /*
     * Ensure we dont read more than one block. The chip will interrupt us
     * When the next block is available.
     */
    count = host->sg_ptr->length - host->sg_off;
    if(count > (1 << data->blksz_bits)) {
        count = 1 << data->blksz_bits;
    }

    DBG("count: %08x, page: %p, offset: %08x flags %08x\n",
        count, host->sg_ptr->page, host->sg_off, data->flags);

    host->sg_off += count;

    /* Transfer the data */
    if(data->flags & MMC_DATA_READ) {
        while(count > 0) {
            /* Read two bytes from SD/MMC controller. */
            *buf = IPAQ_ASIC3_SD_CTRL_DataPort(host->ctl_base);
            buf++;
            count -= 2;
        }
    } else {
        while(count > 0) {
            /* Write two bytes to SD/MMC controller. */
            IPAQ_ASIC3_SD_CTRL_DataPort(host->ctl_base) = *buf;
            buf++;
            count -= 2;
        }
    }

    kunmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ);
    local_irq_restore(flags);

    if(host->sg_off == host->sg_ptr->length) {
        host->sg_ptr++;
        host->sg_off = 0;
        --host->sg_len;
    }

    return;
}

static void
mmc_data_end_irq(struct asic3_mmc_host *host)
{
    struct mmc_data *data = host->data;

    host->data = NULL;

    if(!data){
        printk(DRIVER_NAME": Spurious data end IRQ\n");
        return;
    }

    if (data->error == MMC_ERR_NONE) {
        data->bytes_xfered = data->blocks << data->blksz_bits;
    } else {
        data->bytes_xfered = 0;
    }

    DBG("Completed data request\n");

    IPAQ_ASIC3_SD_CTRL_StopInternal(host->ctl_base) = 0;

    /* Make sure read enable interrupt and write enable interrupt are disabled */
    if(data->flags & MMC_DATA_READ) {
        IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) |= SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE;
    } else {
        IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) |= SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE;
    }

    mmc_finish_request(host);
}

static void
mmc_cmd_irq(struct asic3_mmc_host *host, unsigned int buffer_stat)
{
    struct mmc_command *cmd = host->cmd;
    u8 *buf = (u8 *)cmd->resp;
    u16 data;

    if(!host->cmd) {
        DBG("Spurious CMD irq\n");
        return;
    }

    host->cmd = NULL;
    if(cmd->flags & MMC_RSP_LONG) {
        /* R2 */
        buf[12] = 0xff;
        data = IPAQ_ASIC3_SD_CTRL_Response0(host->ctl_base);
        buf[13] = data & 0xff;
        buf[14] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response1(host->ctl_base);
        buf[15] = data & 0xff;
        buf[8] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response2(host->ctl_base);
        buf[9] = data & 0xff;
        buf[10] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response3(host->ctl_base);
        buf[11] = data & 0xff;
        buf[4] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response4(host->ctl_base);
        buf[5] = data & 0xff;
        buf[6] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response5(host->ctl_base);
        buf[7] = data & 0xff;
        buf[0] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response6(host->ctl_base);
        buf[1] = data & 0xff;
        buf[2] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response7(host->ctl_base);
        buf[3] = data & 0xff;
    } else if(cmd->flags & MMC_RSP_SHORT) {
        /* R1, R1B, R3 */
        data = IPAQ_ASIC3_SD_CTRL_Response0(host->ctl_base);
        buf[0] = data & 0xff;
        buf[1] = data >> 8;
        data = IPAQ_ASIC3_SD_CTRL_Response1(host->ctl_base);
        buf[2] = data & 0xff;
        buf[3] = data >> 8;
    }
    DBG("Response: %08x %08x %08x %08x\n", cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);

    if(buffer_stat & SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT) {
        cmd->error = MMC_ERR_TIMEOUT;
    } else if((buffer_stat & SD_CTRL_BUFFERSTATUS_CRC_ERROR) && (cmd->flags & MMC_RSP_CRC)) {
        cmd->error = MMC_ERR_BADCRC;
    } else if(buffer_stat &
                (
                      SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS
                    | SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR
                    | SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR
                    | SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW
                    | SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW
                    | SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT
                )
    ) {
        DBG("Buffer status ERROR 0x%04x - inside check buffer\n", buffer_stat);
        DBG("detail0 error status 0x%04x\n", IPAQ_ASIC3_SD_CTRL_ErrorStatus0(host->ctl_base));
        DBG("detail1 error status 0x%04x\n", IPAQ_ASIC3_SD_CTRL_ErrorStatus1(host->ctl_base));
        cmd->error = MMC_ERR_FAILED;
    }

    if(cmd->error == MMC_ERR_NONE) {
        switch (cmd->opcode) {
            case SD_APP_SET_BUS_WIDTH:
                if(cmd->arg == SD_BUS_WIDTH_4) {
                    host->clock_for_sd = SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD;
                    IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup(host->ctl_base) =
                              MEM_CARD_OPTION_REQUIRED
                            | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
                            | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
                            | MEM_CARD_OPTION_DATA_XFR_WIDTH_4;
                } else {
                    host->clock_for_sd = 0;
                    IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup(host->ctl_base) =
                              MEM_CARD_OPTION_REQUIRED
                            | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
                            | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
                            | MEM_CARD_OPTION_DATA_XFR_WIDTH_1;
                }
                break;
            case MMC_SELECT_CARD:
                if((cmd->arg >> 16) == 0) {
                    /* We have been deselected. */
                    IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup(host->ctl_base) =
                          MEM_CARD_OPTION_REQUIRED
                        | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
                        | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
                        | MEM_CARD_OPTION_DATA_XFR_WIDTH_1;
                }
        }
    }

    /*
     * If there is data to handle we enable data IRQs here, and we will
     * ultimatley finish the request in the mmc_data_end_irq handler.
     */
    if(host->data && (cmd->error == MMC_ERR_NONE)){
        if(host->data->flags & MMC_DATA_READ) {
            /* Enable the read enable interrupt */
            IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) &=
                ~SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE;
        } else {
            /* Enable the write enable interrupt */
            IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) &=
                ~SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE;
        }
    } else {
        /* There's no data, or we encountered an error, so finish now. */
        mmc_finish_request(host);
    }

    return;
}


static irqreturn_t
mmc_irq(int irq, void *irq_desc, struct pt_regs *regs)
{
    struct asic3_mmc_host *host;
    unsigned int breg, bmask, bstatus, creg, cmask, cstatus;

#   define DONT_CARE_CARD_BITS ( \
         SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 \
       | SD_CTRL_INTMASKCARD_WRITE_PROTECT \
       | SD_CTRL_INTMASKCARD_UNK6 \
       | SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 \
    )
#   define DONT_CARE_BUFFER_BITS ( SD_CTRL_INTMASKBUFFER_UNK7 )

    host = irq_desc;

    /* asic3 bstatus has errors */
    bstatus = IPAQ_ASIC3_SD_CTRL_BufferCtrl(host->ctl_base);
    bmask   = IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base);
    cstatus = IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base);
    cmask   = IPAQ_ASIC3_SD_CTRL_IntMaskCard(host->ctl_base);
    breg    = bstatus & ~bmask & ~DONT_CARE_BUFFER_BITS;
    creg    = cstatus & ~cmask & ~DONT_CARE_CARD_BITS;

    if (!breg && !creg) {
        /* This occurs sometimes for no known reason.  It doesn't hurt
         * anything, so I don't print it.  */
        IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) &= ~breg;
        IPAQ_ASIC3_SD_CTRL_IntMaskCard(host->ctl_base) &= ~creg;
        goto out;
    }

    while (breg || creg) {

        /* XXX TODO: Need to handle errors in breg here. */

        /*
         * Card insert/remove.  The mmc controlling code is stateless.  That
         * is, it doesn't care if it was an insert or a remove.  It treats
         * both the same.
         */
        /* XXX Asic3 has _3 versions of these status bits, too, for a second slot, perhaps? */
        if (creg & (SD_CTRL_CARDSTATUS_CARD_INSERTED_0 | SD_CTRL_CARDSTATUS_CARD_REMOVED_0)) {
            static void hwinit2_irqsafe(struct asic3_mmc_host *host);
            IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base) &=
                ~(SD_CTRL_CARDSTATUS_CARD_REMOVED_0 | SD_CTRL_CARDSTATUS_CARD_INSERTED_0);
            if(creg & SD_CTRL_CARDSTATUS_CARD_INSERTED_0) {
                hwinit2_irqsafe(host);
            }
            mmc_detect_change(host->mmc);
        }

        /* Command completion */
        if (creg & SD_CTRL_CARDSTATUS_RESPONSE_END) {
            IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base) &=
                ~(SD_CTRL_CARDSTATUS_RESPONSE_END);
            mmc_cmd_irq(host, bstatus);
        }

        /* Data transfer */
        if (breg & (SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE | SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE)) {
            IPAQ_ASIC3_SD_CTRL_BufferCtrl(host->ctl_base) &=
                ~(SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE | SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE);
            mmc_data_irq(host);
        }

        /* Data transfer completion */
        if (creg & SD_CTRL_CARDSTATUS_RW_END) {
            IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base) &= ~(SD_CTRL_CARDSTATUS_RW_END);
            mmc_data_end_irq(host);
        }

        /* Check status - keep going until we've handled it all */
        bstatus = IPAQ_ASIC3_SD_CTRL_BufferCtrl(host->ctl_base);
        bmask   = IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base);
        cstatus = IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base);
        cmask   = IPAQ_ASIC3_SD_CTRL_IntMaskCard(host->ctl_base);
        breg    = bstatus & ~bmask & ~DONT_CARE_BUFFER_BITS;
        creg    = cstatus & ~cmask & ~DONT_CARE_CARD_BITS;
    }

out:
    /* Ensure all interrupt sources are cleared */
    IPAQ_ASIC3_SD_CTRL_BufferCtrl(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base) = 0;
    return IRQ_HANDLED;
}

static void
mmc_start_data(struct asic3_mmc_host *host, struct mmc_data *data)
{
    DBG("setup data transfer: blocksize %08x  nr_blocks %d, page: %08x, offset: %08x\n", 1 << data->blksz_bits,
        data->blocks, data->sg->page, data->sg->offset);

    host->sg_len = data->sg_len;
    host->sg_ptr = data->sg;
    host->sg_off = 0;
    host->data   = data;

    /* Set transfer length and blocksize */
    IPAQ_ASIC3_SD_CTRL_TransferSectorCount(host->ctl_base) = data->blocks;
    IPAQ_ASIC3_SD_CTRL_MemCardXferDataLen(host->ctl_base)  = 1 << data->blksz_bits;
}

/* Process requests from the MMC layer */
static void
mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
    struct asic3_mmc_host *host = mmc_priv(mmc);

    WARN_ON(host->mrq != NULL);

    host->mrq = mrq;

    /* If we're performing a data request we need to setup some
       extra information */
    if(mrq->data) {
        mmc_start_data(host, mrq->data);
    }

    mmc_start_command(host, mrq->cmd);
}

/* Set MMC clock / power.
 * Note: This controller uses a simple divider scheme therefore it cannot run
 * a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as MMC
 * wont run that fast, it has to be clocked at 12MHz which is the next slowest
 * setting.  This is likely not an issue because we are doing single 16-bit
 * writes for data I/O.
 */
static void
mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
    struct asic3_mmc_host *host = mmc_priv(mmc);
    u32 clk = 0;

    DBG("clock %uHz busmode %u powermode %u Vdd %u\n",
        ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);

    if (ios->clock) {
        clk = 0x80; /* slowest by default */
        if(ios->clock >= 24000000 / 256) clk >>= 1;
        if(ios->clock >= 24000000 / 128) clk >>= 1;
        if(ios->clock >= 24000000 / 64)  clk >>= 1;
        if(ios->clock >= 24000000 / 32)  clk >>= 1;
        if(ios->clock >= 24000000 / 16)  clk >>= 1;
        if(ios->clock >= 24000000 / 8)   clk >>= 1;
        if(ios->clock >= 24000000 / 4)   clk >>= 1;
        if(ios->clock >= 24000000 / 2)   clk >>= 1;
        if(ios->clock >= 24000000 / 1)   clk >>= 1;
        if(clk == 0) { /* For fastest speed we disable the divider. */
            IPAQ_ASIC3_SD_CONFIG_ClockMode(host->ctl_base) = 0;
        } else {
            IPAQ_ASIC3_SD_CONFIG_ClockMode(host->ctl_base) = 1;
        }
        IPAQ_ASIC3_SD_CTRL_CardClockCtrl(host->ctl_base) = 0;
        IPAQ_ASIC3_SD_CTRL_CardClockCtrl(host->ctl_base) =
              host->clock_for_sd
            | SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK
            | clk;
        msleep(10);
    } else {
        IPAQ_ASIC3_SD_CTRL_CardClockCtrl(host->ctl_base) = 0;
    }

    switch (ios->power_mode) {
        case MMC_POWER_OFF:
            IPAQ_ASIC3_SD_CONFIG_SDHC_Power1(host->ctl_base) = 0;
            msleep(1);
            break;
        case MMC_POWER_UP:
            break;
        case MMC_POWER_ON:
            IPAQ_ASIC3_SD_CONFIG_SDHC_Power1(host->ctl_base) = SD_CONFIG_POWER1_PC_33V;
            msleep(20);
            break;
    }
}

static int
mmc_get_ro(struct mmc_host *mmc)
{
    struct asic3_mmc_host *host = mmc_priv(mmc);

    /* WRITE_PROTECT is active low */
    return (IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base) & SD_CTRL_CARDSTATUS_WRITE_PROTECT)?0:1;
}

static struct mmc_host_ops mmc_ops = {
    .request        = mmc_request,
    .set_ios        = mmc_set_ios,
    .get_ro         = mmc_get_ro,
};

static void
hwinit2_irqsafe(struct asic3_mmc_host *host)
{
    IPAQ_ASIC3_SD_CONFIG_Addr1(host->ctl_base) = 0x0000;
    IPAQ_ASIC3_SD_CONFIG_Addr0(host->ctl_base) = 0x0800;

    IPAQ_ASIC3_SD_CONFIG_ClkStop(host->ctl_base) = SD_CONFIG_CLK_ENABLE_ALL;
    IPAQ_ASIC3_SD_CONFIG_SDHC_CardDetect(host->ctl_base) = 2;
    IPAQ_ASIC3_SD_CONFIG_Command(host->ctl_base) = SD_CONFIG_COMMAND_MAE;

    IPAQ_ASIC3_SD_CTRL_SoftwareReset(host->ctl_base) = 0; /* reset on */
    mdelay(2);

    IPAQ_ASIC3_SD_CTRL_SoftwareReset(host->ctl_base) = 1; /* reset off */
    mdelay(2);

    IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup(host->ctl_base) =
          MEM_CARD_OPTION_REQUIRED
        | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
        | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
        | MEM_CARD_OPTION_DATA_XFR_WIDTH_1
        ;
    host->clock_for_sd = 0;

    IPAQ_ASIC3_SD_CTRL_CardClockCtrl(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_CardStatus(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_BufferCtrl(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_ErrorStatus0(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_ErrorStatus1(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_StopInternal(host->ctl_base) = 0;

    IPAQ_ASIC3_SDIO_CTRL_ClocknWaitCtrl(host->ctl_base) = 0x100;
    /* *((unsigned short *)(((char *)host->ctl_base) + 0x938)) = 0x100; */

    IPAQ_ASIC3_SD_CONFIG_ClockMode(host->ctl_base) = 0;
    IPAQ_ASIC3_SD_CTRL_CardClockCtrl(host->ctl_base) = 0;

    mdelay(1);


    IPAQ_ASIC3_SD_CTRL_IntMaskCard(host->ctl_base) = ~(
          SD_CTRL_INTMASKCARD_RESPONSE_END
        | SD_CTRL_INTMASKCARD_RW_END
        | SD_CTRL_INTMASKCARD_CARD_REMOVED_0
        | SD_CTRL_INTMASKCARD_CARD_INSERTED_0
#if 0
        | SD_CTRL_INTMASKCARD_CARD_REMOVED_3
        | SD_CTRL_INTMASKCARD_CARD_INSERTED_3
#endif
        )
        ; /* check */
    IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(host->ctl_base) = 0xffff;  /* IRQs off */

    /*
     * IPAQ_ASIC3_SD_CTRL_TransactionCtrl(host->ctl_base) = SD_CTRL_TRANSACTIONCONTROL_SET;
     *   Wince has 0x1000
     */
    /* IPAQ_ASIC3_SD_CTRL_TransactionCtrl(host->ctl_base) = 0x1000; */


    IPAQ_ASIC3_SDHWCTRL_SDConf(host->cnf_base) |= ASIC3_SDHWCTRL_SDPWR; /* turn on power at controller(?) */

}

static void
hwinit(struct asic3_mmc_host *host, struct device *dev)
{
    IPAQ_ASIC3_SDHWCTRL_SDConf(host->cnf_base) |= 8;
    IPAQ_ASIC3_SDHWCTRL_SDConf(host->cnf_base) |= 0x10;
    IPAQ_ASIC3_SDHWCTRL_SDConf(host->cnf_base) &= 0xfffe;
    IPAQ_ASIC3_SDHWCTRL_SDConf(host->cnf_base) &= 0xfffb;

    IPAQ_ASIC3_CLOCK_CDEX(host->cnf_base) |= 0x4000;
    IPAQ_ASIC3_CLOCK_CDEX(host->cnf_base) |= 0x2000;
    msleep(1);

    IPAQ_ASIC3_CLOCK_SEL(host->cnf_base)  = 7;

    IPAQ_ASIC3_CLOCK_CDEX(host->cnf_base) |= 0x200;
    IPAQ_ASIC3_CLOCK_CDEX(host->cnf_base) |= 0x400;
    IPAQ_ASIC3_CLOCK_CDEX(host->cnf_base) |= 0xc3;
    msleep(1);

    IPAQ_ASIC3_EXTCF_Select(host->cnf_base) |= 0x4000;

    /* Long Delay */
    mdelay(500);

    hwinit2_irqsafe(host);
}

#ifdef CONFIG_PM
static int
mmc_suspend(struct device *dev, uint32_t state, uint32_t level)
{
    struct mmc_host *mmc = dev_get_drvdata(dev);
    int ret = 0;

    if (mmc && level == SUSPEND_DISABLE)
        ret = mmc_suspend_host(mmc, state);

    return 0;
}

static int
mmc_resume(struct device *dev, uint32_t level)
{
    struct mmc_host *mmc = dev_get_drvdata(dev);
    int ret = 0;

    if (mmc && level == RESUME_ENABLE)
        ret = mmc_resume_host(mmc);

    return ret;
}
#endif

static int
mmc_probe(struct device *dev)
{
    struct mmc_host *mmc;
    struct asic3_mmc_host *host;
    int retval = 0;

    host = 0;

    mmc = mmc_alloc_host(sizeof(struct asic3_mmc_host) + 128, dev);
    if (!mmc) {
        retval = -ENOMEM;
        goto exceptional_return;
    }


    host = mmc_priv(mmc);
    host->mmc = mmc;
    dev_set_drvdata(dev, mmc);

    host->cnf_base = host->ctl_base = 0;
    host->clock_for_sd = 0;

    host->cnf_base = ioremap(0x0c000000, 0x2000);
    if(!host->cnf_base){
        goto exceptional_return;
    }

    host->ctl_base = host->ctl_base = ioremap(0x10000000, 0x2000);
    if(!host->ctl_base){
        goto exceptional_return;
    }

    mmc->ops = &mmc_ops;
    mmc->caps = MMC_CAP_4_BIT_DATA;
    mmc->f_min = 46875; /* ARIC: not sure what these should be */
    mmc->f_max = 24000000; /* ARIC: not sure what these should be */
    mmc->ocr_avail = MMC_VDD_32_33;

    hwinit(host, dev);

    host->irq = IRQ_GPIO(GPIO_NR_H4000_SD_IRQ_N);

    retval = request_irq(host->irq, mmc_irq, SA_INTERRUPT, DRIVER_NAME, host);
    if(retval) {
        printk("Unable to get interrupt\n");
        retval = -ENODEV;
        goto exceptional_return;
    }
    set_irq_type(host->irq, IRQT_FALLING);

    mmc_add_host(mmc);

    return 0;

exceptional_return:
    if (mmc) {
        mmc_free_host(mmc);
    }
    if(host && host->cnf_base) iounmap(host->cnf_base);
    if(host && host->ctl_base) iounmap(host->ctl_base);
    return retval;
}

static int
mmc_remove(struct device *dev)
{
    struct mmc_host *mmc = dev_get_drvdata(dev);
    dev_set_drvdata(dev, NULL);

    if (mmc) {
        struct asic3_mmc_host *host = mmc_priv(mmc);

        mmc_remove_host(mmc);
        free_irq(host->irq, host);
        /* FIXME - we might want to consider stopping the chip here... */
        iounmap(host->ctl_base);
        iounmap(host->cnf_base);
        mmc_free_host(mmc); /* FIXME - why does this call hang? */
    }
    return 0;
}

/* ------------------- device registration ----------------------- */

static struct device_driver mmc_asic3_driver = {
    .name    = DRIVER_NAME,
    .bus     = &platform_bus_type,
    .probe   = mmc_probe,
    .remove  = mmc_remove,
#ifdef CONFIG_PM
    .suspend = mmc_suspend,
    .resume  = mmc_resume,
#endif
};

static int __init mmc_init(void)
{
    if (!machine_is_h4000())
        return -ENODEV;

    driver_register(&mmc_asic3_driver);
    return 0;
}

static void __exit mmc_exit(void)
{
    driver_unregister(&mmc_asic3_driver);
}

late_initcall(mmc_init);
module_exit(mmc_exit);

MODULE_DESCRIPTION("HTC ASIC3 SD/MMC driver");
MODULE_AUTHOR("Aric Blumer, SDG Systems, LLC");
MODULE_LICENSE("GPL");
