/*
* linux/drivers/mmc/host/mxcmmc.c - Freescale i.MX MMCI driver
*
* This is a driver for the SDHC controller found in Freescale MX2/MX3
* SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
* Unlike the hardware found on MX1, this hardware just works and does
* not need all the quirks found in imxmmc.c, hence the separate driver.
*
* Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
*
* derived from pxamci.c by Russell King
*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/dmaengine.h>
#include <linux/types.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <mach/mmc.h>
#include <mach/dma.h>
#include <mach/hardware.h>
#define DRIVER_NAME "mxc-mmc"
#define MMC_REG_STR_STP_CLK 0x00
#define MMC_REG_STATUS 0x04
#define MMC_REG_CLK_RATE 0x08
#define MMC_REG_CMD_DAT_CONT 0x0C
#define MMC_REG_RES_TO 0x10
#define MMC_REG_READ_TO 0x14
#define MMC_REG_BLK_LEN 0x18
#define MMC_REG_NOB 0x1C
#define MMC_REG_REV_NO 0x20
#define MMC_REG_INT_CNTR 0x24
#define MMC_REG_CMD 0x28
#define MMC_REG_ARG 0x2C
#define MMC_REG_RES_FIFO 0x34
#define MMC_REG_BUFFER_ACCESS 0x38
#define STR_STP_CLK_RESET (1 << 3)
#define STR_STP_CLK_START_CLK (1 << 1)
#define STR_STP_CLK_STOP_CLK (1 << 0)
#define STATUS_CARD_INSERTION (1 << 31)
#define STATUS_CARD_REMOVAL (1 << 30)
#define STATUS_YBUF_EMPTY (1 << 29)
#define STATUS_XBUF_EMPTY (1 << 28)
#define STATUS_YBUF_FULL (1 << 27)
#define STATUS_XBUF_FULL (1 << 26)
#define STATUS_BUF_UND_RUN (1 << 25)
#define STATUS_BUF_OVFL (1 << 24)
#define STATUS_SDIO_INT_ACTIVE (1 << 14)
#define STATUS_END_CMD_RESP (1 << 13)
#define STATUS_WRITE_OP_DONE (1 << 12)
#define STATUS_DATA_TRANS_DONE (1 << 11)
#define STATUS_READ_OP_DONE (1 << 11)
#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10)
#define STATUS_CARD_BUS_CLK_RUN (1 << 8)
#define STATUS_BUF_READ_RDY (1 << 7)
#define STATUS_BUF_WRITE_RDY (1 << 6)
#define STATUS_RESP_CRC_ERR (1 << 5)
#define STATUS_CRC_READ_ERR (1 << 3)
#define STATUS_CRC_WRITE_ERR (1 << 2)
#define STATUS_TIME_OUT_RESP (1 << 1)
#define STATUS_TIME_OUT_READ (1 << 0)
#define STATUS_ERR_MASK 0x2f
#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12)
#define CMD_DAT_CONT_STOP_READWAIT (1 << 11)
#define CMD_DAT_CONT_START_READWAIT (1 << 10)
#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8)
#define CMD_DAT_CONT_INIT (1 << 7)
#define CMD_DAT_CONT_WRITE (1 << 4)
#define CMD_DAT_CONT_DATA_ENABLE (1 << 3)
#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0)
#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0)
#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0)
#define INT_SDIO_INT_WKP_EN (1 << 18)
#define INT_CARD_INSERTION_WKP_EN (1 << 17)
#define INT_CARD_REMOVAL_WKP_EN (1 << 16)
#define INT_CARD_INSERTION_EN (1 << 15)
#define INT_CARD_REMOVAL_EN (1 << 14)
#define INT_SDIO_IRQ_EN (1 << 13)
#define INT_DAT0_EN (1 << 12)
#define INT_BUF_READ_EN (1 << 4)
#define INT_BUF_WRITE_EN (1 << 3)
#define INT_END_CMD_RES_EN (1 << 2)
#define INT_WRITE_OP_DONE_EN (1 << 1)
#define INT_READ_OP_EN (1 << 0)
struct mxcmci_host {
struct mmc_host *mmc;
struct resource *res;
void __iomem *base;
int irq;
int detect_irq;
struct dma_chan *dma;
struct dma_async_tx_descriptor *desc;
int do_dma;
int default_irq_mask;
int use_sdio;
unsigned int power_mode;
struct imxmmc_platform_data *pdata;
struct mmc_request *req;
struct mmc_command *cmd;
struct mmc_data *data;
unsigned int datasize;
unsigned int dma_dir;
u16 rev_no;
unsigned int cmdat;
struct clk *clk;
int clock;
struct work_struct datawork;
spinlock_t lock;
struct regulator *vcc;
int burstlen;
int dmareq;
struct dma_slave_config dma_slave_config;
struct imx_dma_data dma_data;
};
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
static inline void mxcmci_init_ocr(struct mxcmci_host *host)
{
host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
if (IS_ERR(host->vcc)) {
host->vcc = NULL;
} else {
host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
if (host->pdata && host->pdata->ocr_avail)
dev_warn(mmc_dev(host->mmc),
"pdata->ocr_avail will not be used\n");
}
if (host->vcc == NULL) {
/* fall-back to platform data */
if (host->pdata && host->pdata->ocr_avail)
host->mmc->ocr_avail = host->pdata->ocr_avail;
else
host->mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
}
}
static inline void mxcmci_set_power(struct mxcmci_host *host,
unsigned char power_mode,
unsigned int vdd)
{
if (host->vcc) {
if (power_mode == MMC_POWER_UP)
mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
else if (power_mode == MMC_POWER_OFF)
mmc_regulator_set_ocr(host->mmc, host->vcc, 0