162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/mmc/host/mxcmmc.c - Freescale i.MX MMCI driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This is a driver for the SDHC controller found in Freescale MX2/MX3 662306a36Sopenharmony_ci * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c). 762306a36Sopenharmony_ci * Unlike the hardware found on MX1, this hardware just works and does 862306a36Sopenharmony_ci * not need all the quirks found in imxmmc.c, hence the separate driver. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> 1162306a36Sopenharmony_ci * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com> 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * derived from pxamci.c by Russell King 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/ioport.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/highmem.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/irq.h> 2362306a36Sopenharmony_ci#include <linux/blkdev.h> 2462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2562306a36Sopenharmony_ci#include <linux/mmc/host.h> 2662306a36Sopenharmony_ci#include <linux/mmc/card.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/clk.h> 2962306a36Sopenharmony_ci#include <linux/io.h> 3062306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3162306a36Sopenharmony_ci#include <linux/dmaengine.h> 3262306a36Sopenharmony_ci#include <linux/types.h> 3362306a36Sopenharmony_ci#include <linux/of.h> 3462306a36Sopenharmony_ci#include <linux/of_dma.h> 3562306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <asm/dma.h> 3862306a36Sopenharmony_ci#include <asm/irq.h> 3962306a36Sopenharmony_ci#include <linux/platform_data/mmc-mxcmmc.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include <linux/dma/imx-dma.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define DRIVER_NAME "mxc-mmc" 4462306a36Sopenharmony_ci#define MXCMCI_TIMEOUT_MS 10000 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define MMC_REG_STR_STP_CLK 0x00 4762306a36Sopenharmony_ci#define MMC_REG_STATUS 0x04 4862306a36Sopenharmony_ci#define MMC_REG_CLK_RATE 0x08 4962306a36Sopenharmony_ci#define MMC_REG_CMD_DAT_CONT 0x0C 5062306a36Sopenharmony_ci#define MMC_REG_RES_TO 0x10 5162306a36Sopenharmony_ci#define MMC_REG_READ_TO 0x14 5262306a36Sopenharmony_ci#define MMC_REG_BLK_LEN 0x18 5362306a36Sopenharmony_ci#define MMC_REG_NOB 0x1C 5462306a36Sopenharmony_ci#define MMC_REG_REV_NO 0x20 5562306a36Sopenharmony_ci#define MMC_REG_INT_CNTR 0x24 5662306a36Sopenharmony_ci#define MMC_REG_CMD 0x28 5762306a36Sopenharmony_ci#define MMC_REG_ARG 0x2C 5862306a36Sopenharmony_ci#define MMC_REG_RES_FIFO 0x34 5962306a36Sopenharmony_ci#define MMC_REG_BUFFER_ACCESS 0x38 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define STR_STP_CLK_RESET (1 << 3) 6262306a36Sopenharmony_ci#define STR_STP_CLK_START_CLK (1 << 1) 6362306a36Sopenharmony_ci#define STR_STP_CLK_STOP_CLK (1 << 0) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define STATUS_CARD_INSERTION (1 << 31) 6662306a36Sopenharmony_ci#define STATUS_CARD_REMOVAL (1 << 30) 6762306a36Sopenharmony_ci#define STATUS_YBUF_EMPTY (1 << 29) 6862306a36Sopenharmony_ci#define STATUS_XBUF_EMPTY (1 << 28) 6962306a36Sopenharmony_ci#define STATUS_YBUF_FULL (1 << 27) 7062306a36Sopenharmony_ci#define STATUS_XBUF_FULL (1 << 26) 7162306a36Sopenharmony_ci#define STATUS_BUF_UND_RUN (1 << 25) 7262306a36Sopenharmony_ci#define STATUS_BUF_OVFL (1 << 24) 7362306a36Sopenharmony_ci#define STATUS_SDIO_INT_ACTIVE (1 << 14) 7462306a36Sopenharmony_ci#define STATUS_END_CMD_RESP (1 << 13) 7562306a36Sopenharmony_ci#define STATUS_WRITE_OP_DONE (1 << 12) 7662306a36Sopenharmony_ci#define STATUS_DATA_TRANS_DONE (1 << 11) 7762306a36Sopenharmony_ci#define STATUS_READ_OP_DONE (1 << 11) 7862306a36Sopenharmony_ci#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10) 7962306a36Sopenharmony_ci#define STATUS_CARD_BUS_CLK_RUN (1 << 8) 8062306a36Sopenharmony_ci#define STATUS_BUF_READ_RDY (1 << 7) 8162306a36Sopenharmony_ci#define STATUS_BUF_WRITE_RDY (1 << 6) 8262306a36Sopenharmony_ci#define STATUS_RESP_CRC_ERR (1 << 5) 8362306a36Sopenharmony_ci#define STATUS_CRC_READ_ERR (1 << 3) 8462306a36Sopenharmony_ci#define STATUS_CRC_WRITE_ERR (1 << 2) 8562306a36Sopenharmony_ci#define STATUS_TIME_OUT_RESP (1 << 1) 8662306a36Sopenharmony_ci#define STATUS_TIME_OUT_READ (1 << 0) 8762306a36Sopenharmony_ci#define STATUS_ERR_MASK 0x2f 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12) 9062306a36Sopenharmony_ci#define CMD_DAT_CONT_STOP_READWAIT (1 << 11) 9162306a36Sopenharmony_ci#define CMD_DAT_CONT_START_READWAIT (1 << 10) 9262306a36Sopenharmony_ci#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8) 9362306a36Sopenharmony_ci#define CMD_DAT_CONT_INIT (1 << 7) 9462306a36Sopenharmony_ci#define CMD_DAT_CONT_WRITE (1 << 4) 9562306a36Sopenharmony_ci#define CMD_DAT_CONT_DATA_ENABLE (1 << 3) 9662306a36Sopenharmony_ci#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0) 9762306a36Sopenharmony_ci#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0) 9862306a36Sopenharmony_ci#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define INT_SDIO_INT_WKP_EN (1 << 18) 10162306a36Sopenharmony_ci#define INT_CARD_INSERTION_WKP_EN (1 << 17) 10262306a36Sopenharmony_ci#define INT_CARD_REMOVAL_WKP_EN (1 << 16) 10362306a36Sopenharmony_ci#define INT_CARD_INSERTION_EN (1 << 15) 10462306a36Sopenharmony_ci#define INT_CARD_REMOVAL_EN (1 << 14) 10562306a36Sopenharmony_ci#define INT_SDIO_IRQ_EN (1 << 13) 10662306a36Sopenharmony_ci#define INT_DAT0_EN (1 << 12) 10762306a36Sopenharmony_ci#define INT_BUF_READ_EN (1 << 4) 10862306a36Sopenharmony_ci#define INT_BUF_WRITE_EN (1 << 3) 10962306a36Sopenharmony_ci#define INT_END_CMD_RES_EN (1 << 2) 11062306a36Sopenharmony_ci#define INT_WRITE_OP_DONE_EN (1 << 1) 11162306a36Sopenharmony_ci#define INT_READ_OP_EN (1 << 0) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cienum mxcmci_type { 11462306a36Sopenharmony_ci IMX21_MMC, 11562306a36Sopenharmony_ci IMX31_MMC, 11662306a36Sopenharmony_ci MPC512X_MMC, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct mxcmci_host { 12062306a36Sopenharmony_ci struct mmc_host *mmc; 12162306a36Sopenharmony_ci void __iomem *base; 12262306a36Sopenharmony_ci dma_addr_t phys_base; 12362306a36Sopenharmony_ci int detect_irq; 12462306a36Sopenharmony_ci struct dma_chan *dma; 12562306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 12662306a36Sopenharmony_ci int do_dma; 12762306a36Sopenharmony_ci int default_irq_mask; 12862306a36Sopenharmony_ci int use_sdio; 12962306a36Sopenharmony_ci unsigned int power_mode; 13062306a36Sopenharmony_ci struct imxmmc_platform_data *pdata; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci struct mmc_request *req; 13362306a36Sopenharmony_ci struct mmc_command *cmd; 13462306a36Sopenharmony_ci struct mmc_data *data; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci unsigned int datasize; 13762306a36Sopenharmony_ci unsigned int dma_dir; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci u16 rev_no; 14062306a36Sopenharmony_ci unsigned int cmdat; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci struct clk *clk_ipg; 14362306a36Sopenharmony_ci struct clk *clk_per; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci int clock; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci struct work_struct datawork; 14862306a36Sopenharmony_ci spinlock_t lock; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci int burstlen; 15162306a36Sopenharmony_ci int dmareq; 15262306a36Sopenharmony_ci struct dma_slave_config dma_slave_config; 15362306a36Sopenharmony_ci struct imx_dma_data dma_data; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci struct timer_list watchdog; 15662306a36Sopenharmony_ci enum mxcmci_type devtype; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const struct of_device_id mxcmci_of_match[] = { 16062306a36Sopenharmony_ci { 16162306a36Sopenharmony_ci .compatible = "fsl,imx21-mmc", 16262306a36Sopenharmony_ci .data = (void *) IMX21_MMC, 16362306a36Sopenharmony_ci }, { 16462306a36Sopenharmony_ci .compatible = "fsl,imx31-mmc", 16562306a36Sopenharmony_ci .data = (void *) IMX31_MMC, 16662306a36Sopenharmony_ci }, { 16762306a36Sopenharmony_ci .compatible = "fsl,mpc5121-sdhc", 16862306a36Sopenharmony_ci .data = (void *) MPC512X_MMC, 16962306a36Sopenharmony_ci }, { 17062306a36Sopenharmony_ci /* sentinel */ 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxcmci_of_match); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic inline int is_imx31_mmc(struct mxcmci_host *host) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci return host->devtype == IMX31_MMC; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic inline int is_mpc512x_mmc(struct mxcmci_host *host) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci return host->devtype == MPC512X_MMC; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic inline u32 mxcmci_readl(struct mxcmci_host *host, int reg) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_MPC512x)) 18862306a36Sopenharmony_ci return ioread32be(host->base + reg); 18962306a36Sopenharmony_ci else 19062306a36Sopenharmony_ci return readl(host->base + reg); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic inline void mxcmci_writel(struct mxcmci_host *host, u32 val, int reg) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_MPC512x)) 19662306a36Sopenharmony_ci iowrite32be(val, host->base + reg); 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci writel(val, host->base + reg); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic inline u16 mxcmci_readw(struct mxcmci_host *host, int reg) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_MPC512x)) 20462306a36Sopenharmony_ci return ioread32be(host->base + reg); 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci return readw(host->base + reg); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_MPC512x)) 21262306a36Sopenharmony_ci iowrite32be(val, host->base + reg); 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci writew(val, host->base + reg); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void mxcmci_set_power(struct mxcmci_host *host, unsigned int vdd) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if (!IS_ERR(host->mmc->supply.vmmc)) { 22262306a36Sopenharmony_ci if (host->power_mode == MMC_POWER_UP) 22362306a36Sopenharmony_ci mmc_regulator_set_ocr(host->mmc, 22462306a36Sopenharmony_ci host->mmc->supply.vmmc, vdd); 22562306a36Sopenharmony_ci else if (host->power_mode == MMC_POWER_OFF) 22662306a36Sopenharmony_ci mmc_regulator_set_ocr(host->mmc, 22762306a36Sopenharmony_ci host->mmc->supply.vmmc, 0); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (host->pdata && host->pdata->setpower) 23162306a36Sopenharmony_ci host->pdata->setpower(mmc_dev(host->mmc), vdd); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic inline int mxcmci_use_dma(struct mxcmci_host *host) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return host->do_dma; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void mxcmci_softreset(struct mxcmci_host *host) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci int i; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "mxcmci_softreset\n"); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* reset sequence */ 24662306a36Sopenharmony_ci mxcmci_writew(host, STR_STP_CLK_RESET, MMC_REG_STR_STP_CLK); 24762306a36Sopenharmony_ci mxcmci_writew(host, STR_STP_CLK_RESET | STR_STP_CLK_START_CLK, 24862306a36Sopenharmony_ci MMC_REG_STR_STP_CLK); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (i = 0; i < 8; i++) 25162306a36Sopenharmony_ci mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci mxcmci_writew(host, 0xff, MMC_REG_RES_TO); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PPC_MPC512x) 25762306a36Sopenharmony_cistatic inline void buffer_swap32(u32 *buf, int len) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int i; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (i = 0; i < ((len + 3) / 4); i++) { 26262306a36Sopenharmony_ci *buf = swab32(*buf); 26362306a36Sopenharmony_ci buf++; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void mxcmci_swap_buffers(struct mmc_data *data) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct scatterlist *sg; 27062306a36Sopenharmony_ci int i; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci for_each_sg(data->sg, sg, data->sg_len, i) 27362306a36Sopenharmony_ci buffer_swap32(sg_virt(sg), sg->length); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci#else 27662306a36Sopenharmony_cistatic inline void mxcmci_swap_buffers(struct mmc_data *data) {} 27762306a36Sopenharmony_ci#endif 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci unsigned int nob = data->blocks; 28262306a36Sopenharmony_ci unsigned int blksz = data->blksz; 28362306a36Sopenharmony_ci unsigned int datasize = nob * blksz; 28462306a36Sopenharmony_ci struct scatterlist *sg; 28562306a36Sopenharmony_ci enum dma_transfer_direction slave_dirn; 28662306a36Sopenharmony_ci int i, nents; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci host->data = data; 28962306a36Sopenharmony_ci data->bytes_xfered = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mxcmci_writew(host, nob, MMC_REG_NOB); 29262306a36Sopenharmony_ci mxcmci_writew(host, blksz, MMC_REG_BLK_LEN); 29362306a36Sopenharmony_ci host->datasize = datasize; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!mxcmci_use_dma(host)) 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci for_each_sg(data->sg, sg, data->sg_len, i) { 29962306a36Sopenharmony_ci if (sg->offset & 3 || sg->length & 3 || sg->length < 512) { 30062306a36Sopenharmony_ci host->do_dma = 0; 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) { 30662306a36Sopenharmony_ci host->dma_dir = DMA_FROM_DEVICE; 30762306a36Sopenharmony_ci slave_dirn = DMA_DEV_TO_MEM; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci host->dma_dir = DMA_TO_DEVICE; 31062306a36Sopenharmony_ci slave_dirn = DMA_MEM_TO_DEV; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci mxcmci_swap_buffers(data); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci nents = dma_map_sg(host->dma->device->dev, data->sg, 31662306a36Sopenharmony_ci data->sg_len, host->dma_dir); 31762306a36Sopenharmony_ci if (nents != data->sg_len) 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci host->desc = dmaengine_prep_slave_sg(host->dma, 32162306a36Sopenharmony_ci data->sg, data->sg_len, slave_dirn, 32262306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!host->desc) { 32562306a36Sopenharmony_ci dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len, 32662306a36Sopenharmony_ci host->dma_dir); 32762306a36Sopenharmony_ci host->do_dma = 0; 32862306a36Sopenharmony_ci return 0; /* Fall back to PIO */ 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci wmb(); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci dmaengine_submit(host->desc); 33362306a36Sopenharmony_ci dma_async_issue_pending(host->dma); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci mod_timer(&host->watchdog, jiffies + msecs_to_jiffies(MXCMCI_TIMEOUT_MS)); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat); 34162306a36Sopenharmony_cistatic void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic void mxcmci_dma_callback(void *data) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct mxcmci_host *host = data; 34662306a36Sopenharmony_ci u32 stat; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci del_timer(&host->watchdog); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci stat = mxcmci_readl(host, MMC_REG_STATUS); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci mxcmci_data_done(host, stat); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd, 35862306a36Sopenharmony_ci unsigned int cmdat) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci u32 int_cntr = host->default_irq_mask; 36162306a36Sopenharmony_ci unsigned long flags; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci WARN_ON(host->cmd != NULL); 36462306a36Sopenharmony_ci host->cmd = cmd; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci switch (mmc_resp_type(cmd)) { 36762306a36Sopenharmony_ci case MMC_RSP_R1: /* short CRC, OPCODE */ 36862306a36Sopenharmony_ci case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */ 36962306a36Sopenharmony_ci cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case MMC_RSP_R2: /* long 136 bit + CRC */ 37262306a36Sopenharmony_ci cmdat |= CMD_DAT_CONT_RESPONSE_136BIT; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case MMC_RSP_R3: /* short */ 37562306a36Sopenharmony_ci cmdat |= CMD_DAT_CONT_RESPONSE_48BIT; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci case MMC_RSP_NONE: 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci default: 38062306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "unhandled response type 0x%x\n", 38162306a36Sopenharmony_ci mmc_resp_type(cmd)); 38262306a36Sopenharmony_ci cmd->error = -EINVAL; 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci int_cntr = INT_END_CMD_RES_EN; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (mxcmci_use_dma(host)) { 38962306a36Sopenharmony_ci if (host->dma_dir == DMA_FROM_DEVICE) { 39062306a36Sopenharmony_ci host->desc->callback = mxcmci_dma_callback; 39162306a36Sopenharmony_ci host->desc->callback_param = host; 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci int_cntr |= INT_WRITE_OP_DONE_EN; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 39862306a36Sopenharmony_ci if (host->use_sdio) 39962306a36Sopenharmony_ci int_cntr |= INT_SDIO_IRQ_EN; 40062306a36Sopenharmony_ci mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR); 40162306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci mxcmci_writew(host, cmd->opcode, MMC_REG_CMD); 40462306a36Sopenharmony_ci mxcmci_writel(host, cmd->arg, MMC_REG_ARG); 40562306a36Sopenharmony_ci mxcmci_writew(host, cmdat, MMC_REG_CMD_DAT_CONT); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void mxcmci_finish_request(struct mxcmci_host *host, 41162306a36Sopenharmony_ci struct mmc_request *req) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci u32 int_cntr = host->default_irq_mask; 41462306a36Sopenharmony_ci unsigned long flags; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 41762306a36Sopenharmony_ci if (host->use_sdio) 41862306a36Sopenharmony_ci int_cntr |= INT_SDIO_IRQ_EN; 41962306a36Sopenharmony_ci mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR); 42062306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci host->req = NULL; 42362306a36Sopenharmony_ci host->cmd = NULL; 42462306a36Sopenharmony_ci host->data = NULL; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mmc_request_done(host->mmc, req); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct mmc_data *data = host->data; 43262306a36Sopenharmony_ci int data_error; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (mxcmci_use_dma(host)) { 43562306a36Sopenharmony_ci dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len, 43662306a36Sopenharmony_ci host->dma_dir); 43762306a36Sopenharmony_ci mxcmci_swap_buffers(data); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (stat & STATUS_ERR_MASK) { 44162306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", 44262306a36Sopenharmony_ci stat); 44362306a36Sopenharmony_ci if (stat & STATUS_CRC_READ_ERR) { 44462306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "%s: -EILSEQ\n", __func__); 44562306a36Sopenharmony_ci data->error = -EILSEQ; 44662306a36Sopenharmony_ci } else if (stat & STATUS_CRC_WRITE_ERR) { 44762306a36Sopenharmony_ci u32 err_code = (stat >> 9) & 0x3; 44862306a36Sopenharmony_ci if (err_code == 2) { /* No CRC response */ 44962306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 45062306a36Sopenharmony_ci "%s: No CRC -ETIMEDOUT\n", __func__); 45162306a36Sopenharmony_ci data->error = -ETIMEDOUT; 45262306a36Sopenharmony_ci } else { 45362306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 45462306a36Sopenharmony_ci "%s: -EILSEQ\n", __func__); 45562306a36Sopenharmony_ci data->error = -EILSEQ; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci } else if (stat & STATUS_TIME_OUT_READ) { 45862306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 45962306a36Sopenharmony_ci "%s: read -ETIMEDOUT\n", __func__); 46062306a36Sopenharmony_ci data->error = -ETIMEDOUT; 46162306a36Sopenharmony_ci } else { 46262306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "%s: -EIO\n", __func__); 46362306a36Sopenharmony_ci data->error = -EIO; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci data->bytes_xfered = host->datasize; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci data_error = data->error; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci host->data = NULL; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return data_error; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void mxcmci_read_response(struct mxcmci_host *host, unsigned int stat) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct mmc_command *cmd = host->cmd; 47962306a36Sopenharmony_ci int i; 48062306a36Sopenharmony_ci u32 a, b, c; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!cmd) 48362306a36Sopenharmony_ci return; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (stat & STATUS_TIME_OUT_RESP) { 48662306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); 48762306a36Sopenharmony_ci cmd->error = -ETIMEDOUT; 48862306a36Sopenharmony_ci } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { 48962306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); 49062306a36Sopenharmony_ci cmd->error = -EILSEQ; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_PRESENT) { 49462306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_136) { 49562306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 49662306a36Sopenharmony_ci a = mxcmci_readw(host, MMC_REG_RES_FIFO); 49762306a36Sopenharmony_ci b = mxcmci_readw(host, MMC_REG_RES_FIFO); 49862306a36Sopenharmony_ci cmd->resp[i] = a << 16 | b; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci a = mxcmci_readw(host, MMC_REG_RES_FIFO); 50262306a36Sopenharmony_ci b = mxcmci_readw(host, MMC_REG_RES_FIFO); 50362306a36Sopenharmony_ci c = mxcmci_readw(host, MMC_REG_RES_FIFO); 50462306a36Sopenharmony_ci cmd->resp[0] = a << 24 | b << 8 | c >> 8; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int mxcmci_poll_status(struct mxcmci_host *host, u32 mask) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci u32 stat; 51262306a36Sopenharmony_ci unsigned long timeout = jiffies + HZ; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci do { 51562306a36Sopenharmony_ci stat = mxcmci_readl(host, MMC_REG_STATUS); 51662306a36Sopenharmony_ci if (stat & STATUS_ERR_MASK) 51762306a36Sopenharmony_ci return stat; 51862306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 51962306a36Sopenharmony_ci mxcmci_softreset(host); 52062306a36Sopenharmony_ci mxcmci_set_clk_rate(host, host->clock); 52162306a36Sopenharmony_ci return STATUS_TIME_OUT_READ; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci if (stat & mask) 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci cpu_relax(); 52662306a36Sopenharmony_ci } while (1); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci unsigned int stat; 53262306a36Sopenharmony_ci u32 *buf = _buf; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci while (bytes > 3) { 53562306a36Sopenharmony_ci stat = mxcmci_poll_status(host, 53662306a36Sopenharmony_ci STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); 53762306a36Sopenharmony_ci if (stat) 53862306a36Sopenharmony_ci return stat; 53962306a36Sopenharmony_ci *buf++ = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS)); 54062306a36Sopenharmony_ci bytes -= 4; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (bytes) { 54462306a36Sopenharmony_ci u8 *b = (u8 *)buf; 54562306a36Sopenharmony_ci u32 tmp; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci stat = mxcmci_poll_status(host, 54862306a36Sopenharmony_ci STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); 54962306a36Sopenharmony_ci if (stat) 55062306a36Sopenharmony_ci return stat; 55162306a36Sopenharmony_ci tmp = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS)); 55262306a36Sopenharmony_ci memcpy(b, &tmp, bytes); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci unsigned int stat; 56162306a36Sopenharmony_ci u32 *buf = _buf; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci while (bytes > 3) { 56462306a36Sopenharmony_ci stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); 56562306a36Sopenharmony_ci if (stat) 56662306a36Sopenharmony_ci return stat; 56762306a36Sopenharmony_ci mxcmci_writel(host, cpu_to_le32(*buf++), MMC_REG_BUFFER_ACCESS); 56862306a36Sopenharmony_ci bytes -= 4; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (bytes) { 57262306a36Sopenharmony_ci u8 *b = (u8 *)buf; 57362306a36Sopenharmony_ci u32 tmp; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); 57662306a36Sopenharmony_ci if (stat) 57762306a36Sopenharmony_ci return stat; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci memcpy(&tmp, b, bytes); 58062306a36Sopenharmony_ci mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int mxcmci_transfer_data(struct mxcmci_host *host) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct mmc_data *data = host->req->data; 58962306a36Sopenharmony_ci struct scatterlist *sg; 59062306a36Sopenharmony_ci int stat, i; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci host->data = data; 59362306a36Sopenharmony_ci host->datasize = 0; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) { 59662306a36Sopenharmony_ci for_each_sg(data->sg, sg, data->sg_len, i) { 59762306a36Sopenharmony_ci stat = mxcmci_pull(host, sg_virt(sg), sg->length); 59862306a36Sopenharmony_ci if (stat) 59962306a36Sopenharmony_ci return stat; 60062306a36Sopenharmony_ci host->datasize += sg->length; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci for_each_sg(data->sg, sg, data->sg_len, i) { 60462306a36Sopenharmony_ci stat = mxcmci_push(host, sg_virt(sg), sg->length); 60562306a36Sopenharmony_ci if (stat) 60662306a36Sopenharmony_ci return stat; 60762306a36Sopenharmony_ci host->datasize += sg->length; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE); 61062306a36Sopenharmony_ci if (stat) 61162306a36Sopenharmony_ci return stat; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void mxcmci_datawork(struct work_struct *work) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct mxcmci_host *host = container_of(work, struct mxcmci_host, 61962306a36Sopenharmony_ci datawork); 62062306a36Sopenharmony_ci int datastat = mxcmci_transfer_data(host); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE, 62362306a36Sopenharmony_ci MMC_REG_STATUS); 62462306a36Sopenharmony_ci mxcmci_finish_data(host, datastat); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (host->req->stop) { 62762306a36Sopenharmony_ci if (mxcmci_start_cmd(host, host->req->stop, 0)) { 62862306a36Sopenharmony_ci mxcmci_finish_request(host, host->req); 62962306a36Sopenharmony_ci return; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci mxcmci_finish_request(host, host->req); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct mmc_request *req; 63962306a36Sopenharmony_ci int data_error; 64062306a36Sopenharmony_ci unsigned long flags; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!host->data) { 64562306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 64662306a36Sopenharmony_ci return; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (!host->req) { 65062306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci req = host->req; 65562306a36Sopenharmony_ci if (!req->stop) 65662306a36Sopenharmony_ci host->req = NULL; /* we will handle finish req below */ 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci data_error = mxcmci_finish_data(host, stat); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (data_error) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci mxcmci_read_response(host, stat); 66662306a36Sopenharmony_ci host->cmd = NULL; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (req->stop) { 66962306a36Sopenharmony_ci if (mxcmci_start_cmd(host, req->stop, 0)) { 67062306a36Sopenharmony_ci mxcmci_finish_request(host, req); 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci } else { 67462306a36Sopenharmony_ci mxcmci_finish_request(host, req); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci mxcmci_read_response(host, stat); 68162306a36Sopenharmony_ci host->cmd = NULL; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!host->data && host->req) { 68462306a36Sopenharmony_ci mxcmci_finish_request(host, host->req); 68562306a36Sopenharmony_ci return; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* For the DMA case the DMA engine handles the data transfer 68962306a36Sopenharmony_ci * automatically. For non DMA we have to do it ourselves. 69062306a36Sopenharmony_ci * Don't do it in interrupt context though. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci if (!mxcmci_use_dma(host) && host->data) 69362306a36Sopenharmony_ci schedule_work(&host->datawork); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic irqreturn_t mxcmci_irq(int irq, void *devid) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct mxcmci_host *host = devid; 70062306a36Sopenharmony_ci bool sdio_irq; 70162306a36Sopenharmony_ci u32 stat; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci stat = mxcmci_readl(host, MMC_REG_STATUS); 70462306a36Sopenharmony_ci mxcmci_writel(host, 70562306a36Sopenharmony_ci stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE | 70662306a36Sopenharmony_ci STATUS_WRITE_OP_DONE), 70762306a36Sopenharmony_ci MMC_REG_STATUS); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci spin_lock(&host->lock); 71262306a36Sopenharmony_ci sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio; 71362306a36Sopenharmony_ci spin_unlock(&host->lock); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE))) 71662306a36Sopenharmony_ci mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (sdio_irq) { 71962306a36Sopenharmony_ci mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS); 72062306a36Sopenharmony_ci mmc_signal_sdio_irq(host->mmc); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (stat & STATUS_END_CMD_RESP) 72462306a36Sopenharmony_ci mxcmci_cmd_done(host, stat); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) { 72762306a36Sopenharmony_ci del_timer(&host->watchdog); 72862306a36Sopenharmony_ci mxcmci_data_done(host, stat); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (host->default_irq_mask && 73262306a36Sopenharmony_ci (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL))) 73362306a36Sopenharmony_ci mmc_detect_change(host->mmc, msecs_to_jiffies(200)); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return IRQ_HANDLED; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 74162306a36Sopenharmony_ci unsigned int cmdat = host->cmdat; 74262306a36Sopenharmony_ci int error; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci WARN_ON(host->req != NULL); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci host->req = req; 74762306a36Sopenharmony_ci host->cmdat &= ~CMD_DAT_CONT_INIT; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (host->dma) 75062306a36Sopenharmony_ci host->do_dma = 1; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (req->data) { 75362306a36Sopenharmony_ci error = mxcmci_setup_data(host, req->data); 75462306a36Sopenharmony_ci if (error) { 75562306a36Sopenharmony_ci req->cmd->error = error; 75662306a36Sopenharmony_ci goto out; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci cmdat |= CMD_DAT_CONT_DATA_ENABLE; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (req->data->flags & MMC_DATA_WRITE) 76362306a36Sopenharmony_ci cmdat |= CMD_DAT_CONT_WRITE; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci error = mxcmci_start_cmd(host, req->cmd, cmdat); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciout: 76962306a36Sopenharmony_ci if (error) 77062306a36Sopenharmony_ci mxcmci_finish_request(host, req); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci unsigned int divider; 77662306a36Sopenharmony_ci int prescaler = 0; 77762306a36Sopenharmony_ci unsigned int clk_in = clk_get_rate(host->clk_per); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci while (prescaler <= 0x800) { 78062306a36Sopenharmony_ci for (divider = 1; divider <= 0xF; divider++) { 78162306a36Sopenharmony_ci int x; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci x = (clk_in / (divider + 1)); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (prescaler) 78662306a36Sopenharmony_ci x /= (prescaler * 2); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (x <= clk_ios) 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci if (divider < 0x10) 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (prescaler == 0) 79562306a36Sopenharmony_ci prescaler = 1; 79662306a36Sopenharmony_ci else 79762306a36Sopenharmony_ci prescaler <<= 1; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci mxcmci_writew(host, (prescaler << 4) | divider, MMC_REG_CLK_RATE); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "scaler: %d divider: %d in: %d out: %d\n", 80362306a36Sopenharmony_ci prescaler, divider, clk_in, clk_ios); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int mxcmci_setup_dma(struct mmc_host *mmc) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 80962306a36Sopenharmony_ci struct dma_slave_config *config = &host->dma_slave_config; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci config->dst_addr = host->phys_base + MMC_REG_BUFFER_ACCESS; 81262306a36Sopenharmony_ci config->src_addr = host->phys_base + MMC_REG_BUFFER_ACCESS; 81362306a36Sopenharmony_ci config->dst_addr_width = 4; 81462306a36Sopenharmony_ci config->src_addr_width = 4; 81562306a36Sopenharmony_ci config->dst_maxburst = host->burstlen; 81662306a36Sopenharmony_ci config->src_maxburst = host->burstlen; 81762306a36Sopenharmony_ci config->device_fc = false; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return dmaengine_slave_config(host->dma, config); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 82562306a36Sopenharmony_ci int burstlen, ret; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* 82862306a36Sopenharmony_ci * use burstlen of 64 (16 words) in 4 bit mode (--> reg value 0) 82962306a36Sopenharmony_ci * use burstlen of 16 (4 words) in 1 bit mode (--> reg value 16) 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci if (ios->bus_width == MMC_BUS_WIDTH_4) 83262306a36Sopenharmony_ci burstlen = 16; 83362306a36Sopenharmony_ci else 83462306a36Sopenharmony_ci burstlen = 4; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (mxcmci_use_dma(host) && burstlen != host->burstlen) { 83762306a36Sopenharmony_ci host->burstlen = burstlen; 83862306a36Sopenharmony_ci ret = mxcmci_setup_dma(mmc); 83962306a36Sopenharmony_ci if (ret) { 84062306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 84162306a36Sopenharmony_ci "failed to config DMA channel. Falling back to PIO\n"); 84262306a36Sopenharmony_ci dma_release_channel(host->dma); 84362306a36Sopenharmony_ci host->do_dma = 0; 84462306a36Sopenharmony_ci host->dma = NULL; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (ios->bus_width == MMC_BUS_WIDTH_4) 84962306a36Sopenharmony_ci host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; 85062306a36Sopenharmony_ci else 85162306a36Sopenharmony_ci host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (host->power_mode != ios->power_mode) { 85462306a36Sopenharmony_ci host->power_mode = ios->power_mode; 85562306a36Sopenharmony_ci mxcmci_set_power(host, ios->vdd); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (ios->power_mode == MMC_POWER_ON) 85862306a36Sopenharmony_ci host->cmdat |= CMD_DAT_CONT_INIT; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (ios->clock) { 86262306a36Sopenharmony_ci mxcmci_set_clk_rate(host, ios->clock); 86362306a36Sopenharmony_ci mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK); 86462306a36Sopenharmony_ci } else { 86562306a36Sopenharmony_ci mxcmci_writew(host, STR_STP_CLK_STOP_CLK, MMC_REG_STR_STP_CLK); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci host->clock = ios->clock; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic irqreturn_t mxcmci_detect_irq(int irq, void *data) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct mmc_host *mmc = data; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s\n", __func__); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci mmc_detect_change(mmc, msecs_to_jiffies(250)); 87862306a36Sopenharmony_ci return IRQ_HANDLED; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int mxcmci_get_ro(struct mmc_host *mmc) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (host->pdata && host->pdata->get_ro) 88662306a36Sopenharmony_ci return !!host->pdata->get_ro(mmc_dev(mmc)); 88762306a36Sopenharmony_ci /* 88862306a36Sopenharmony_ci * If board doesn't support read only detection (no mmc_gpio 88962306a36Sopenharmony_ci * context or gpio is invalid), then let the mmc core decide 89062306a36Sopenharmony_ci * what to do. 89162306a36Sopenharmony_ci */ 89262306a36Sopenharmony_ci return mmc_gpio_get_ro(mmc); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 89862306a36Sopenharmony_ci unsigned long flags; 89962306a36Sopenharmony_ci u32 int_cntr; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 90262306a36Sopenharmony_ci host->use_sdio = enable; 90362306a36Sopenharmony_ci int_cntr = mxcmci_readl(host, MMC_REG_INT_CNTR); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (enable) 90662306a36Sopenharmony_ci int_cntr |= INT_SDIO_IRQ_EN; 90762306a36Sopenharmony_ci else 90862306a36Sopenharmony_ci int_cntr &= ~INT_SDIO_IRQ_EN; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR); 91162306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct mxcmci_host *mxcmci = mmc_priv(host); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* 91962306a36Sopenharmony_ci * MX3 SoCs have a silicon bug which corrupts CRC calculation of 92062306a36Sopenharmony_ci * multi-block transfers when connected SDIO peripheral doesn't 92162306a36Sopenharmony_ci * drive the BUSY line as required by the specs. 92262306a36Sopenharmony_ci * One way to prevent this is to only allow 1-bit transfers. 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (is_imx31_mmc(mxcmci) && mmc_card_sdio(card)) 92662306a36Sopenharmony_ci host->caps &= ~MMC_CAP_4_BIT_DATA; 92762306a36Sopenharmony_ci else 92862306a36Sopenharmony_ci host->caps |= MMC_CAP_4_BIT_DATA; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic bool filter(struct dma_chan *chan, void *param) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct mxcmci_host *host = param; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (!imx_dma_is_general_purpose(chan)) 93662306a36Sopenharmony_ci return false; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci chan->private = &host->dma_data; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return true; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void mxcmci_watchdog(struct timer_list *t) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct mxcmci_host *host = from_timer(host, t, watchdog); 94662306a36Sopenharmony_ci struct mmc_request *req = host->req; 94762306a36Sopenharmony_ci unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (host->dma_dir == DMA_FROM_DEVICE) { 95062306a36Sopenharmony_ci dmaengine_terminate_all(host->dma); 95162306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 95262306a36Sopenharmony_ci "%s: read time out (status = 0x%08x)\n", 95362306a36Sopenharmony_ci __func__, stat); 95462306a36Sopenharmony_ci } else { 95562306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 95662306a36Sopenharmony_ci "%s: write time out (status = 0x%08x)\n", 95762306a36Sopenharmony_ci __func__, stat); 95862306a36Sopenharmony_ci mxcmci_softreset(host); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* Mark transfer as erroneus and inform the upper layers */ 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (host->data) 96462306a36Sopenharmony_ci host->data->error = -ETIMEDOUT; 96562306a36Sopenharmony_ci host->req = NULL; 96662306a36Sopenharmony_ci host->cmd = NULL; 96762306a36Sopenharmony_ci host->data = NULL; 96862306a36Sopenharmony_ci mmc_request_done(host->mmc, req); 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic const struct mmc_host_ops mxcmci_ops = { 97262306a36Sopenharmony_ci .request = mxcmci_request, 97362306a36Sopenharmony_ci .set_ios = mxcmci_set_ios, 97462306a36Sopenharmony_ci .get_ro = mxcmci_get_ro, 97562306a36Sopenharmony_ci .enable_sdio_irq = mxcmci_enable_sdio_irq, 97662306a36Sopenharmony_ci .init_card = mxcmci_init_card, 97762306a36Sopenharmony_ci}; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic int mxcmci_probe(struct platform_device *pdev) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct mmc_host *mmc; 98262306a36Sopenharmony_ci struct mxcmci_host *host; 98362306a36Sopenharmony_ci struct resource *res; 98462306a36Sopenharmony_ci int ret = 0, irq; 98562306a36Sopenharmony_ci bool dat3_card_detect = false; 98662306a36Sopenharmony_ci dma_cap_mask_t mask; 98762306a36Sopenharmony_ci struct imxmmc_platform_data *pdata = pdev->dev.platform_data; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci pr_info("i.MX/MPC512x SDHC driver\n"); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 99262306a36Sopenharmony_ci if (irq < 0) 99362306a36Sopenharmony_ci return irq; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); 99662306a36Sopenharmony_ci if (!mmc) 99762306a36Sopenharmony_ci return -ENOMEM; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci host = mmc_priv(mmc); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 100262306a36Sopenharmony_ci if (IS_ERR(host->base)) { 100362306a36Sopenharmony_ci ret = PTR_ERR(host->base); 100462306a36Sopenharmony_ci goto out_free; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci host->phys_base = res->start; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci ret = mmc_of_parse(mmc); 101062306a36Sopenharmony_ci if (ret) 101162306a36Sopenharmony_ci goto out_free; 101262306a36Sopenharmony_ci mmc->ops = &mxcmci_ops; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* For devicetree parsing, the bus width is read from devicetree */ 101562306a36Sopenharmony_ci if (pdata) 101662306a36Sopenharmony_ci mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; 101762306a36Sopenharmony_ci else 101862306a36Sopenharmony_ci mmc->caps |= MMC_CAP_SDIO_IRQ; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* MMC core transfer sizes tunable parameters */ 102162306a36Sopenharmony_ci mmc->max_blk_size = 2048; 102262306a36Sopenharmony_ci mmc->max_blk_count = 65535; 102362306a36Sopenharmony_ci mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 102462306a36Sopenharmony_ci mmc->max_seg_size = mmc->max_req_size; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci host->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* adjust max_segs after devtype detection */ 102962306a36Sopenharmony_ci if (!is_mpc512x_mmc(host)) 103062306a36Sopenharmony_ci mmc->max_segs = 64; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci host->mmc = mmc; 103362306a36Sopenharmony_ci host->pdata = pdata; 103462306a36Sopenharmony_ci spin_lock_init(&host->lock); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (pdata) 103762306a36Sopenharmony_ci dat3_card_detect = pdata->dat3_card_detect; 103862306a36Sopenharmony_ci else if (mmc_card_is_removable(mmc) 103962306a36Sopenharmony_ci && !of_property_read_bool(pdev->dev.of_node, "cd-gpios")) 104062306a36Sopenharmony_ci dat3_card_detect = true; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci ret = mmc_regulator_get_supply(mmc); 104362306a36Sopenharmony_ci if (ret) 104462306a36Sopenharmony_ci goto out_free; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (!mmc->ocr_avail) { 104762306a36Sopenharmony_ci if (pdata && pdata->ocr_avail) 104862306a36Sopenharmony_ci mmc->ocr_avail = pdata->ocr_avail; 104962306a36Sopenharmony_ci else 105062306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (dat3_card_detect) 105462306a36Sopenharmony_ci host->default_irq_mask = 105562306a36Sopenharmony_ci INT_CARD_INSERTION_EN | INT_CARD_REMOVAL_EN; 105662306a36Sopenharmony_ci else 105762306a36Sopenharmony_ci host->default_irq_mask = 0; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci host->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 106062306a36Sopenharmony_ci if (IS_ERR(host->clk_ipg)) { 106162306a36Sopenharmony_ci ret = PTR_ERR(host->clk_ipg); 106262306a36Sopenharmony_ci goto out_free; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci host->clk_per = devm_clk_get(&pdev->dev, "per"); 106662306a36Sopenharmony_ci if (IS_ERR(host->clk_per)) { 106762306a36Sopenharmony_ci ret = PTR_ERR(host->clk_per); 106862306a36Sopenharmony_ci goto out_free; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk_per); 107262306a36Sopenharmony_ci if (ret) 107362306a36Sopenharmony_ci goto out_free; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk_ipg); 107662306a36Sopenharmony_ci if (ret) 107762306a36Sopenharmony_ci goto out_clk_per_put; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci mxcmci_softreset(host); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci host->rev_no = mxcmci_readw(host, MMC_REG_REV_NO); 108262306a36Sopenharmony_ci if (host->rev_no != 0x400) { 108362306a36Sopenharmony_ci ret = -ENODEV; 108462306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n", 108562306a36Sopenharmony_ci host->rev_no); 108662306a36Sopenharmony_ci goto out_clk_put; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci mmc->f_min = clk_get_rate(host->clk_per) >> 16; 109062306a36Sopenharmony_ci mmc->f_max = clk_get_rate(host->clk_per) >> 1; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* recommended in data sheet */ 109362306a36Sopenharmony_ci mxcmci_writew(host, 0x2db4, MMC_REG_READ_TO); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci mxcmci_writel(host, host->default_irq_mask, MMC_REG_INT_CNTR); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (!host->pdata) { 109862306a36Sopenharmony_ci host->dma = dma_request_chan(&pdev->dev, "rx-tx"); 109962306a36Sopenharmony_ci if (IS_ERR(host->dma)) { 110062306a36Sopenharmony_ci if (PTR_ERR(host->dma) == -EPROBE_DEFER) { 110162306a36Sopenharmony_ci ret = -EPROBE_DEFER; 110262306a36Sopenharmony_ci goto out_clk_put; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci /* Ignore errors to fall back to PIO mode */ 110662306a36Sopenharmony_ci host->dma = NULL; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci } else { 110962306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_DMA, 0); 111062306a36Sopenharmony_ci if (res) { 111162306a36Sopenharmony_ci host->dmareq = res->start; 111262306a36Sopenharmony_ci host->dma_data.peripheral_type = IMX_DMATYPE_SDHC; 111362306a36Sopenharmony_ci host->dma_data.priority = DMA_PRIO_LOW; 111462306a36Sopenharmony_ci host->dma_data.dma_request = host->dmareq; 111562306a36Sopenharmony_ci dma_cap_zero(mask); 111662306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 111762306a36Sopenharmony_ci host->dma = dma_request_channel(mask, filter, host); 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci if (host->dma) 112162306a36Sopenharmony_ci mmc->max_seg_size = dma_get_max_seg_size( 112262306a36Sopenharmony_ci host->dma->device->dev); 112362306a36Sopenharmony_ci else 112462306a36Sopenharmony_ci dev_info(mmc_dev(host->mmc), "dma not available. Using PIO\n"); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci INIT_WORK(&host->datawork, mxcmci_datawork); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, mxcmci_irq, 0, 112962306a36Sopenharmony_ci dev_name(&pdev->dev), host); 113062306a36Sopenharmony_ci if (ret) 113162306a36Sopenharmony_ci goto out_free_dma; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci platform_set_drvdata(pdev, mmc); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (host->pdata && host->pdata->init) { 113662306a36Sopenharmony_ci ret = host->pdata->init(&pdev->dev, mxcmci_detect_irq, 113762306a36Sopenharmony_ci host->mmc); 113862306a36Sopenharmony_ci if (ret) 113962306a36Sopenharmony_ci goto out_free_dma; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci timer_setup(&host->watchdog, mxcmci_watchdog, 0); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci ret = mmc_add_host(mmc); 114562306a36Sopenharmony_ci if (ret) 114662306a36Sopenharmony_ci goto out_free_dma; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci return 0; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ciout_free_dma: 115162306a36Sopenharmony_ci if (host->dma) 115262306a36Sopenharmony_ci dma_release_channel(host->dma); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ciout_clk_put: 115562306a36Sopenharmony_ci clk_disable_unprepare(host->clk_ipg); 115662306a36Sopenharmony_ciout_clk_per_put: 115762306a36Sopenharmony_ci clk_disable_unprepare(host->clk_per); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ciout_free: 116062306a36Sopenharmony_ci mmc_free_host(mmc); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return ret; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic void mxcmci_remove(struct platform_device *pdev) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci struct mmc_host *mmc = platform_get_drvdata(pdev); 116862306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci mmc_remove_host(mmc); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (host->pdata && host->pdata->exit) 117362306a36Sopenharmony_ci host->pdata->exit(&pdev->dev, mmc); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (host->dma) 117662306a36Sopenharmony_ci dma_release_channel(host->dma); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci clk_disable_unprepare(host->clk_per); 117962306a36Sopenharmony_ci clk_disable_unprepare(host->clk_ipg); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci mmc_free_host(mmc); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic int mxcmci_suspend(struct device *dev) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct mmc_host *mmc = dev_get_drvdata(dev); 118762306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci clk_disable_unprepare(host->clk_per); 119062306a36Sopenharmony_ci clk_disable_unprepare(host->clk_ipg); 119162306a36Sopenharmony_ci return 0; 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic int mxcmci_resume(struct device *dev) 119562306a36Sopenharmony_ci{ 119662306a36Sopenharmony_ci struct mmc_host *mmc = dev_get_drvdata(dev); 119762306a36Sopenharmony_ci struct mxcmci_host *host = mmc_priv(mmc); 119862306a36Sopenharmony_ci int ret; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk_per); 120162306a36Sopenharmony_ci if (ret) 120262306a36Sopenharmony_ci return ret; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk_ipg); 120562306a36Sopenharmony_ci if (ret) 120662306a36Sopenharmony_ci clk_disable_unprepare(host->clk_per); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return ret; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic struct platform_driver mxcmci_driver = { 121462306a36Sopenharmony_ci .probe = mxcmci_probe, 121562306a36Sopenharmony_ci .remove_new = mxcmci_remove, 121662306a36Sopenharmony_ci .driver = { 121762306a36Sopenharmony_ci .name = DRIVER_NAME, 121862306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 121962306a36Sopenharmony_ci .pm = pm_sleep_ptr(&mxcmci_pm_ops), 122062306a36Sopenharmony_ci .of_match_table = mxcmci_of_match, 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci}; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cimodule_platform_driver(mxcmci_driver); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver"); 122762306a36Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer, Pengutronix"); 122862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 122962306a36Sopenharmony_ciMODULE_ALIAS("platform:mxc-mmc"); 1230