162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * WM8505/WM8650 SD/MMC Host Controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Tony Prisk 662306a36Sopenharmony_ci * Copyright (C) 2008 WonderMedia Technologies, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/ioport.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/irq.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/of_address.h> 2362306a36Sopenharmony_ci#include <linux/of_irq.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/mmc/host.h> 2662306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 2762306a36Sopenharmony_ci#include <linux/mmc/sd.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/byteorder.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DRIVER_NAME "wmt-sdhc" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* MMC/SD controller registers */ 3662306a36Sopenharmony_ci#define SDMMC_CTLR 0x00 3762306a36Sopenharmony_ci#define SDMMC_CMD 0x01 3862306a36Sopenharmony_ci#define SDMMC_RSPTYPE 0x02 3962306a36Sopenharmony_ci#define SDMMC_ARG 0x04 4062306a36Sopenharmony_ci#define SDMMC_BUSMODE 0x08 4162306a36Sopenharmony_ci#define SDMMC_BLKLEN 0x0C 4262306a36Sopenharmony_ci#define SDMMC_BLKCNT 0x0E 4362306a36Sopenharmony_ci#define SDMMC_RSP 0x10 4462306a36Sopenharmony_ci#define SDMMC_CBCR 0x20 4562306a36Sopenharmony_ci#define SDMMC_INTMASK0 0x24 4662306a36Sopenharmony_ci#define SDMMC_INTMASK1 0x25 4762306a36Sopenharmony_ci#define SDMMC_STS0 0x28 4862306a36Sopenharmony_ci#define SDMMC_STS1 0x29 4962306a36Sopenharmony_ci#define SDMMC_STS2 0x2A 5062306a36Sopenharmony_ci#define SDMMC_STS3 0x2B 5162306a36Sopenharmony_ci#define SDMMC_RSPTIMEOUT 0x2C 5262306a36Sopenharmony_ci#define SDMMC_CLK 0x30 /* VT8500 only */ 5362306a36Sopenharmony_ci#define SDMMC_EXTCTRL 0x34 5462306a36Sopenharmony_ci#define SDMMC_SBLKLEN 0x38 5562306a36Sopenharmony_ci#define SDMMC_DMATIMEOUT 0x3C 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* SDMMC_CTLR bit fields */ 5962306a36Sopenharmony_ci#define CTLR_CMD_START 0x01 6062306a36Sopenharmony_ci#define CTLR_CMD_WRITE 0x04 6162306a36Sopenharmony_ci#define CTLR_FIFO_RESET 0x08 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* SDMMC_BUSMODE bit fields */ 6462306a36Sopenharmony_ci#define BM_SPI_MODE 0x01 6562306a36Sopenharmony_ci#define BM_FOURBIT_MODE 0x02 6662306a36Sopenharmony_ci#define BM_EIGHTBIT_MODE 0x04 6762306a36Sopenharmony_ci#define BM_SD_OFF 0x10 6862306a36Sopenharmony_ci#define BM_SPI_CS 0x20 6962306a36Sopenharmony_ci#define BM_SD_POWER 0x40 7062306a36Sopenharmony_ci#define BM_SOFT_RESET 0x80 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* SDMMC_BLKLEN bit fields */ 7362306a36Sopenharmony_ci#define BLKL_CRCERR_ABORT 0x0800 7462306a36Sopenharmony_ci#define BLKL_CD_POL_HIGH 0x1000 7562306a36Sopenharmony_ci#define BLKL_GPI_CD 0x2000 7662306a36Sopenharmony_ci#define BLKL_DATA3_CD 0x4000 7762306a36Sopenharmony_ci#define BLKL_INT_ENABLE 0x8000 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* SDMMC_INTMASK0 bit fields */ 8062306a36Sopenharmony_ci#define INT0_MBLK_TRAN_DONE_INT_EN 0x10 8162306a36Sopenharmony_ci#define INT0_BLK_TRAN_DONE_INT_EN 0x20 8262306a36Sopenharmony_ci#define INT0_CD_INT_EN 0x40 8362306a36Sopenharmony_ci#define INT0_DI_INT_EN 0x80 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* SDMMC_INTMASK1 bit fields */ 8662306a36Sopenharmony_ci#define INT1_CMD_RES_TRAN_DONE_INT_EN 0x02 8762306a36Sopenharmony_ci#define INT1_CMD_RES_TOUT_INT_EN 0x04 8862306a36Sopenharmony_ci#define INT1_MBLK_AUTO_STOP_INT_EN 0x08 8962306a36Sopenharmony_ci#define INT1_DATA_TOUT_INT_EN 0x10 9062306a36Sopenharmony_ci#define INT1_RESCRC_ERR_INT_EN 0x20 9162306a36Sopenharmony_ci#define INT1_RCRC_ERR_INT_EN 0x40 9262306a36Sopenharmony_ci#define INT1_WCRC_ERR_INT_EN 0x80 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* SDMMC_STS0 bit fields */ 9562306a36Sopenharmony_ci#define STS0_WRITE_PROTECT 0x02 9662306a36Sopenharmony_ci#define STS0_CD_DATA3 0x04 9762306a36Sopenharmony_ci#define STS0_CD_GPI 0x08 9862306a36Sopenharmony_ci#define STS0_MBLK_DONE 0x10 9962306a36Sopenharmony_ci#define STS0_BLK_DONE 0x20 10062306a36Sopenharmony_ci#define STS0_CARD_DETECT 0x40 10162306a36Sopenharmony_ci#define STS0_DEVICE_INS 0x80 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* SDMMC_STS1 bit fields */ 10462306a36Sopenharmony_ci#define STS1_SDIO_INT 0x01 10562306a36Sopenharmony_ci#define STS1_CMDRSP_DONE 0x02 10662306a36Sopenharmony_ci#define STS1_RSP_TIMEOUT 0x04 10762306a36Sopenharmony_ci#define STS1_AUTOSTOP_DONE 0x08 10862306a36Sopenharmony_ci#define STS1_DATA_TIMEOUT 0x10 10962306a36Sopenharmony_ci#define STS1_RSP_CRC_ERR 0x20 11062306a36Sopenharmony_ci#define STS1_RCRC_ERR 0x40 11162306a36Sopenharmony_ci#define STS1_WCRC_ERR 0x80 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* SDMMC_STS2 bit fields */ 11462306a36Sopenharmony_ci#define STS2_CMD_RES_BUSY 0x10 11562306a36Sopenharmony_ci#define STS2_DATARSP_BUSY 0x20 11662306a36Sopenharmony_ci#define STS2_DIS_FORCECLK 0x80 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* SDMMC_EXTCTRL bit fields */ 11962306a36Sopenharmony_ci#define EXT_EIGHTBIT 0x04 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* MMC/SD DMA Controller Registers */ 12262306a36Sopenharmony_ci#define SDDMA_GCR 0x100 12362306a36Sopenharmony_ci#define SDDMA_IER 0x104 12462306a36Sopenharmony_ci#define SDDMA_ISR 0x108 12562306a36Sopenharmony_ci#define SDDMA_DESPR 0x10C 12662306a36Sopenharmony_ci#define SDDMA_RBR 0x110 12762306a36Sopenharmony_ci#define SDDMA_DAR 0x114 12862306a36Sopenharmony_ci#define SDDMA_BAR 0x118 12962306a36Sopenharmony_ci#define SDDMA_CPR 0x11C 13062306a36Sopenharmony_ci#define SDDMA_CCR 0x120 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* SDDMA_GCR bit fields */ 13462306a36Sopenharmony_ci#define DMA_GCR_DMA_EN 0x00000001 13562306a36Sopenharmony_ci#define DMA_GCR_SOFT_RESET 0x00000100 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* SDDMA_IER bit fields */ 13862306a36Sopenharmony_ci#define DMA_IER_INT_EN 0x00000001 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* SDDMA_ISR bit fields */ 14162306a36Sopenharmony_ci#define DMA_ISR_INT_STS 0x00000001 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* SDDMA_RBR bit fields */ 14462306a36Sopenharmony_ci#define DMA_RBR_FORMAT 0x40000000 14562306a36Sopenharmony_ci#define DMA_RBR_END 0x80000000 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* SDDMA_CCR bit fields */ 14862306a36Sopenharmony_ci#define DMA_CCR_RUN 0x00000080 14962306a36Sopenharmony_ci#define DMA_CCR_IF_TO_PERIPHERAL 0x00000000 15062306a36Sopenharmony_ci#define DMA_CCR_PERIPHERAL_TO_IF 0x00400000 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* SDDMA_CCR event status */ 15362306a36Sopenharmony_ci#define DMA_CCR_EVT_NO_STATUS 0x00000000 15462306a36Sopenharmony_ci#define DMA_CCR_EVT_UNDERRUN 0x00000001 15562306a36Sopenharmony_ci#define DMA_CCR_EVT_OVERRUN 0x00000002 15662306a36Sopenharmony_ci#define DMA_CCR_EVT_DESP_READ 0x00000003 15762306a36Sopenharmony_ci#define DMA_CCR_EVT_DATA_RW 0x00000004 15862306a36Sopenharmony_ci#define DMA_CCR_EVT_EARLY_END 0x00000005 15962306a36Sopenharmony_ci#define DMA_CCR_EVT_SUCCESS 0x0000000F 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define PDMA_READ 0x00 16262306a36Sopenharmony_ci#define PDMA_WRITE 0x01 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define WMT_SD_POWER_OFF 0 16562306a36Sopenharmony_ci#define WMT_SD_POWER_ON 1 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct wmt_dma_descriptor { 16862306a36Sopenharmony_ci u32 flags; 16962306a36Sopenharmony_ci u32 data_buffer_addr; 17062306a36Sopenharmony_ci u32 branch_addr; 17162306a36Sopenharmony_ci u32 reserved1; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistruct wmt_mci_caps { 17562306a36Sopenharmony_ci unsigned int f_min; 17662306a36Sopenharmony_ci unsigned int f_max; 17762306a36Sopenharmony_ci u32 ocr_avail; 17862306a36Sopenharmony_ci u32 caps; 17962306a36Sopenharmony_ci u32 max_seg_size; 18062306a36Sopenharmony_ci u32 max_segs; 18162306a36Sopenharmony_ci u32 max_blk_size; 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistruct wmt_mci_priv { 18562306a36Sopenharmony_ci struct mmc_host *mmc; 18662306a36Sopenharmony_ci void __iomem *sdmmc_base; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci int irq_regular; 18962306a36Sopenharmony_ci int irq_dma; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci void *dma_desc_buffer; 19262306a36Sopenharmony_ci dma_addr_t dma_desc_device_addr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci struct completion cmdcomp; 19562306a36Sopenharmony_ci struct completion datacomp; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci struct completion *comp_cmd; 19862306a36Sopenharmony_ci struct completion *comp_dma; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci struct mmc_request *req; 20162306a36Sopenharmony_ci struct mmc_command *cmd; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci struct clk *clk_sdmmc; 20462306a36Sopenharmony_ci struct device *dev; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci u8 power_inverted; 20762306a36Sopenharmony_ci u8 cd_inverted; 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (enable ^ priv->power_inverted) 21562306a36Sopenharmony_ci reg_tmp &= ~BM_SD_OFF; 21662306a36Sopenharmony_ci else 21762306a36Sopenharmony_ci reg_tmp |= BM_SD_OFF; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void wmt_mci_read_response(struct mmc_host *mmc) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct wmt_mci_priv *priv; 22562306a36Sopenharmony_ci int idx1, idx2; 22662306a36Sopenharmony_ci u8 tmp_resp; 22762306a36Sopenharmony_ci u32 response; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci priv = mmc_priv(mmc); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (idx1 = 0; idx1 < 4; idx1++) { 23262306a36Sopenharmony_ci response = 0; 23362306a36Sopenharmony_ci for (idx2 = 0; idx2 < 4; idx2++) { 23462306a36Sopenharmony_ci if ((idx1 == 3) && (idx2 == 3)) 23562306a36Sopenharmony_ci tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP); 23662306a36Sopenharmony_ci else 23762306a36Sopenharmony_ci tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP + 23862306a36Sopenharmony_ci (idx1*4) + idx2 + 1); 23962306a36Sopenharmony_ci response |= (tmp_resp << (idx2 * 8)); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci priv->cmd->resp[idx1] = cpu_to_be32(response); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void wmt_mci_start_command(struct wmt_mci_priv *priv) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci u32 reg_tmp; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); 25062306a36Sopenharmony_ci writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype, 25462306a36Sopenharmony_ci u32 arg, u8 rsptype) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct wmt_mci_priv *priv; 25762306a36Sopenharmony_ci u32 reg_tmp; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci priv = mmc_priv(mmc); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* write command, arg, resptype registers */ 26262306a36Sopenharmony_ci writeb(command, priv->sdmmc_base + SDMMC_CMD); 26362306a36Sopenharmony_ci writel(arg, priv->sdmmc_base + SDMMC_ARG); 26462306a36Sopenharmony_ci writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* reset response FIFO */ 26762306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); 26862306a36Sopenharmony_ci writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* ensure clock enabled - VT3465 */ 27162306a36Sopenharmony_ci wmt_set_sd_power(priv, WMT_SD_POWER_ON); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* clear status bits */ 27462306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); 27562306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); 27662306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS2); 27762306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS3); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* set command type */ 28062306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); 28162306a36Sopenharmony_ci writeb((reg_tmp & 0x0F) | (cmdtype << 4), 28262306a36Sopenharmony_ci priv->sdmmc_base + SDMMC_CTLR); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void wmt_mci_disable_dma(struct wmt_mci_priv *priv) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR); 29062306a36Sopenharmony_ci writel(0, priv->sdmmc_base + SDDMA_IER); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void wmt_complete_data_request(struct wmt_mci_priv *priv) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct mmc_request *req; 29662306a36Sopenharmony_ci req = priv->req; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci req->data->bytes_xfered = req->data->blksz * req->data->blocks; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* unmap the DMA pages used for write data */ 30162306a36Sopenharmony_ci if (req->data->flags & MMC_DATA_WRITE) 30262306a36Sopenharmony_ci dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, 30362306a36Sopenharmony_ci req->data->sg_len, DMA_TO_DEVICE); 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, 30662306a36Sopenharmony_ci req->data->sg_len, DMA_FROM_DEVICE); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Check if the DMA ISR returned a data error */ 30962306a36Sopenharmony_ci if ((req->cmd->error) || (req->data->error)) 31062306a36Sopenharmony_ci mmc_request_done(priv->mmc, req); 31162306a36Sopenharmony_ci else { 31262306a36Sopenharmony_ci wmt_mci_read_response(priv->mmc); 31362306a36Sopenharmony_ci if (!req->data->stop) { 31462306a36Sopenharmony_ci /* single-block read/write requests end here */ 31562306a36Sopenharmony_ci mmc_request_done(priv->mmc, req); 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * we change the priv->cmd variable so the response is 31962306a36Sopenharmony_ci * stored in the stop struct rather than the original 32062306a36Sopenharmony_ci * calling command struct 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci priv->comp_cmd = &priv->cmdcomp; 32362306a36Sopenharmony_ci init_completion(priv->comp_cmd); 32462306a36Sopenharmony_ci priv->cmd = req->data->stop; 32562306a36Sopenharmony_ci wmt_mci_send_command(priv->mmc, req->data->stop->opcode, 32662306a36Sopenharmony_ci 7, req->data->stop->arg, 9); 32762306a36Sopenharmony_ci wmt_mci_start_command(priv); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic irqreturn_t wmt_mci_dma_isr(int irq_num, void *data) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct wmt_mci_priv *priv; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci int status; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci priv = (struct wmt_mci_priv *)data; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (status != DMA_CCR_EVT_SUCCESS) { 34362306a36Sopenharmony_ci dev_err(priv->dev, "DMA Error: Status = %d\n", status); 34462306a36Sopenharmony_ci priv->req->data->error = -ETIMEDOUT; 34562306a36Sopenharmony_ci complete(priv->comp_dma); 34662306a36Sopenharmony_ci return IRQ_HANDLED; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci priv->req->data->error = 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci wmt_mci_disable_dma(priv); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci complete(priv->comp_dma); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (priv->comp_cmd) { 35662306a36Sopenharmony_ci if (completion_done(priv->comp_cmd)) { 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * if the command (regular) interrupt has already 35962306a36Sopenharmony_ci * completed, finish off the request otherwise we wait 36062306a36Sopenharmony_ci * for the command interrupt and finish from there. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci wmt_complete_data_request(priv); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return IRQ_HANDLED; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic irqreturn_t wmt_mci_regular_isr(int irq_num, void *data) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct wmt_mci_priv *priv; 37262306a36Sopenharmony_ci u32 status0; 37362306a36Sopenharmony_ci u32 status1; 37462306a36Sopenharmony_ci u32 status2; 37562306a36Sopenharmony_ci u32 reg_tmp; 37662306a36Sopenharmony_ci int cmd_done; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci priv = (struct wmt_mci_priv *)data; 37962306a36Sopenharmony_ci cmd_done = 0; 38062306a36Sopenharmony_ci status0 = readb(priv->sdmmc_base + SDMMC_STS0); 38162306a36Sopenharmony_ci status1 = readb(priv->sdmmc_base + SDMMC_STS1); 38262306a36Sopenharmony_ci status2 = readb(priv->sdmmc_base + SDMMC_STS2); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Check for card insertion */ 38562306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); 38662306a36Sopenharmony_ci if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) { 38762306a36Sopenharmony_ci mmc_detect_change(priv->mmc, 0); 38862306a36Sopenharmony_ci if (priv->cmd) 38962306a36Sopenharmony_ci priv->cmd->error = -ETIMEDOUT; 39062306a36Sopenharmony_ci if (priv->comp_cmd) 39162306a36Sopenharmony_ci complete(priv->comp_cmd); 39262306a36Sopenharmony_ci if (priv->comp_dma) { 39362306a36Sopenharmony_ci wmt_mci_disable_dma(priv); 39462306a36Sopenharmony_ci complete(priv->comp_dma); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0); 39762306a36Sopenharmony_ci return IRQ_HANDLED; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if ((!priv->req->data) || 40162306a36Sopenharmony_ci ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) { 40262306a36Sopenharmony_ci /* handle non-data & stop_transmission requests */ 40362306a36Sopenharmony_ci if (status1 & STS1_CMDRSP_DONE) { 40462306a36Sopenharmony_ci priv->cmd->error = 0; 40562306a36Sopenharmony_ci cmd_done = 1; 40662306a36Sopenharmony_ci } else if ((status1 & STS1_RSP_TIMEOUT) || 40762306a36Sopenharmony_ci (status1 & STS1_DATA_TIMEOUT)) { 40862306a36Sopenharmony_ci priv->cmd->error = -ETIMEDOUT; 40962306a36Sopenharmony_ci cmd_done = 1; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (cmd_done) { 41362306a36Sopenharmony_ci priv->comp_cmd = NULL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!priv->cmd->error) 41662306a36Sopenharmony_ci wmt_mci_read_response(priv->mmc); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci priv->cmd = NULL; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci mmc_request_done(priv->mmc, priv->req); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } else { 42362306a36Sopenharmony_ci /* handle data requests */ 42462306a36Sopenharmony_ci if (status1 & STS1_CMDRSP_DONE) { 42562306a36Sopenharmony_ci if (priv->cmd) 42662306a36Sopenharmony_ci priv->cmd->error = 0; 42762306a36Sopenharmony_ci if (priv->comp_cmd) 42862306a36Sopenharmony_ci complete(priv->comp_cmd); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if ((status1 & STS1_RSP_TIMEOUT) || 43262306a36Sopenharmony_ci (status1 & STS1_DATA_TIMEOUT)) { 43362306a36Sopenharmony_ci if (priv->cmd) 43462306a36Sopenharmony_ci priv->cmd->error = -ETIMEDOUT; 43562306a36Sopenharmony_ci if (priv->comp_cmd) 43662306a36Sopenharmony_ci complete(priv->comp_cmd); 43762306a36Sopenharmony_ci if (priv->comp_dma) { 43862306a36Sopenharmony_ci wmt_mci_disable_dma(priv); 43962306a36Sopenharmony_ci complete(priv->comp_dma); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (priv->comp_dma) { 44462306a36Sopenharmony_ci /* 44562306a36Sopenharmony_ci * If the dma interrupt has already completed, finish 44662306a36Sopenharmony_ci * off the request; otherwise we wait for the DMA 44762306a36Sopenharmony_ci * interrupt and finish from there. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci if (completion_done(priv->comp_dma)) 45062306a36Sopenharmony_ci wmt_complete_data_request(priv); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci writeb(status0, priv->sdmmc_base + SDMMC_STS0); 45562306a36Sopenharmony_ci writeb(status1, priv->sdmmc_base + SDMMC_STS1); 45662306a36Sopenharmony_ci writeb(status2, priv->sdmmc_base + SDMMC_STS2); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return IRQ_HANDLED; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void wmt_reset_hardware(struct mmc_host *mmc) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct wmt_mci_priv *priv; 46462306a36Sopenharmony_ci u32 reg_tmp; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci priv = mmc_priv(mmc); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* reset controller */ 46962306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); 47062306a36Sopenharmony_ci writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* reset response FIFO */ 47362306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); 47462306a36Sopenharmony_ci writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* enable GPI pin to detect card */ 47762306a36Sopenharmony_ci writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* clear interrupt status */ 48062306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); 48162306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* setup interrupts */ 48462306a36Sopenharmony_ci writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base + 48562306a36Sopenharmony_ci SDMMC_INTMASK0); 48662306a36Sopenharmony_ci writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN | 48762306a36Sopenharmony_ci INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* set the DMA timeout */ 49062306a36Sopenharmony_ci writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* auto clock freezing enable */ 49362306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2); 49462306a36Sopenharmony_ci writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* set a default clock speed of 400Khz */ 49762306a36Sopenharmony_ci clk_set_rate(priv->clk_sdmmc, 400000); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int wmt_dma_init(struct mmc_host *mmc) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct wmt_mci_priv *priv; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci priv = mmc_priv(mmc); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR); 50762306a36Sopenharmony_ci writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR); 50862306a36Sopenharmony_ci if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0) 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci else 51162306a36Sopenharmony_ci return 1; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc, 51562306a36Sopenharmony_ci u16 req_count, u32 buffer_addr, u32 branch_addr, int end) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci desc->flags = 0x40000000 | req_count; 51862306a36Sopenharmony_ci if (end) 51962306a36Sopenharmony_ci desc->flags |= 0x80000000; 52062306a36Sopenharmony_ci desc->data_buffer_addr = buffer_addr; 52162306a36Sopenharmony_ci desc->branch_addr = branch_addr; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct wmt_mci_priv *priv; 52762306a36Sopenharmony_ci u32 reg_tmp; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci priv = mmc_priv(mmc); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Enable DMA Interrupts */ 53262306a36Sopenharmony_ci writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Write DMA Descriptor Pointer Register */ 53562306a36Sopenharmony_ci writel(descaddr, priv->sdmmc_base + SDDMA_DESPR); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci writel(0x00, priv->sdmmc_base + SDDMA_CCR); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (dir == PDMA_WRITE) { 54062306a36Sopenharmony_ci reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); 54162306a36Sopenharmony_ci writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base + 54262306a36Sopenharmony_ci SDDMA_CCR); 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); 54562306a36Sopenharmony_ci writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base + 54662306a36Sopenharmony_ci SDDMA_CCR); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void wmt_dma_start(struct wmt_mci_priv *priv) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci u32 reg_tmp; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); 55562306a36Sopenharmony_ci writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct wmt_mci_priv *priv; 56162306a36Sopenharmony_ci struct wmt_dma_descriptor *desc; 56262306a36Sopenharmony_ci u8 command; 56362306a36Sopenharmony_ci u8 cmdtype; 56462306a36Sopenharmony_ci u32 arg; 56562306a36Sopenharmony_ci u8 rsptype; 56662306a36Sopenharmony_ci u32 reg_tmp; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci struct scatterlist *sg; 56962306a36Sopenharmony_ci int i; 57062306a36Sopenharmony_ci int sg_cnt; 57162306a36Sopenharmony_ci int offset; 57262306a36Sopenharmony_ci u32 dma_address; 57362306a36Sopenharmony_ci int desc_cnt; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci priv = mmc_priv(mmc); 57662306a36Sopenharmony_ci priv->req = req; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * Use the cmd variable to pass a pointer to the resp[] structure 58062306a36Sopenharmony_ci * This is required on multi-block requests to pass the pointer to the 58162306a36Sopenharmony_ci * stop command 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci priv->cmd = req->cmd; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci command = req->cmd->opcode; 58662306a36Sopenharmony_ci arg = req->cmd->arg; 58762306a36Sopenharmony_ci rsptype = mmc_resp_type(req->cmd); 58862306a36Sopenharmony_ci cmdtype = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* rsptype=7 only valid for SPI commands - should be =2 for SD */ 59162306a36Sopenharmony_ci if (rsptype == 7) 59262306a36Sopenharmony_ci rsptype = 2; 59362306a36Sopenharmony_ci /* rsptype=21 is R1B, convert for controller */ 59462306a36Sopenharmony_ci if (rsptype == 21) 59562306a36Sopenharmony_ci rsptype = 9; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!req->data) { 59862306a36Sopenharmony_ci wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); 59962306a36Sopenharmony_ci wmt_mci_start_command(priv); 60062306a36Sopenharmony_ci /* completion is now handled in the regular_isr() */ 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci if (req->data) { 60362306a36Sopenharmony_ci priv->comp_cmd = &priv->cmdcomp; 60462306a36Sopenharmony_ci init_completion(priv->comp_cmd); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci wmt_dma_init(mmc); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* set controller data length */ 60962306a36Sopenharmony_ci reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); 61062306a36Sopenharmony_ci writew((reg_tmp & 0xF800) | (req->data->blksz - 1), 61162306a36Sopenharmony_ci priv->sdmmc_base + SDMMC_BLKLEN); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* set controller block count */ 61462306a36Sopenharmony_ci writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (req->data->flags & MMC_DATA_WRITE) { 61962306a36Sopenharmony_ci sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, 62062306a36Sopenharmony_ci req->data->sg_len, DMA_TO_DEVICE); 62162306a36Sopenharmony_ci cmdtype = 1; 62262306a36Sopenharmony_ci if (req->data->blocks > 1) 62362306a36Sopenharmony_ci cmdtype = 3; 62462306a36Sopenharmony_ci } else { 62562306a36Sopenharmony_ci sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, 62662306a36Sopenharmony_ci req->data->sg_len, DMA_FROM_DEVICE); 62762306a36Sopenharmony_ci cmdtype = 2; 62862306a36Sopenharmony_ci if (req->data->blocks > 1) 62962306a36Sopenharmony_ci cmdtype = 4; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci dma_address = priv->dma_desc_device_addr + 16; 63362306a36Sopenharmony_ci desc_cnt = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci for_each_sg(req->data->sg, sg, sg_cnt, i) { 63662306a36Sopenharmony_ci offset = 0; 63762306a36Sopenharmony_ci while (offset < sg_dma_len(sg)) { 63862306a36Sopenharmony_ci wmt_dma_init_descriptor(desc, req->data->blksz, 63962306a36Sopenharmony_ci sg_dma_address(sg)+offset, 64062306a36Sopenharmony_ci dma_address, 0); 64162306a36Sopenharmony_ci desc++; 64262306a36Sopenharmony_ci desc_cnt++; 64362306a36Sopenharmony_ci offset += req->data->blksz; 64462306a36Sopenharmony_ci dma_address += 16; 64562306a36Sopenharmony_ci if (desc_cnt == req->data->blocks) 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci desc--; 65062306a36Sopenharmony_ci desc->flags |= 0x80000000; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (req->data->flags & MMC_DATA_WRITE) 65362306a36Sopenharmony_ci wmt_dma_config(mmc, priv->dma_desc_device_addr, 65462306a36Sopenharmony_ci PDMA_WRITE); 65562306a36Sopenharmony_ci else 65662306a36Sopenharmony_ci wmt_dma_config(mmc, priv->dma_desc_device_addr, 65762306a36Sopenharmony_ci PDMA_READ); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci priv->comp_dma = &priv->datacomp; 66262306a36Sopenharmony_ci init_completion(priv->comp_dma); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci wmt_dma_start(priv); 66562306a36Sopenharmony_ci wmt_mci_start_command(priv); 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct wmt_mci_priv *priv; 67262306a36Sopenharmony_ci u32 busmode, extctrl; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci priv = mmc_priv(mmc); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (ios->power_mode == MMC_POWER_UP) { 67762306a36Sopenharmony_ci wmt_reset_hardware(mmc); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci wmt_set_sd_power(priv, WMT_SD_POWER_ON); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci if (ios->power_mode == MMC_POWER_OFF) 68262306a36Sopenharmony_ci wmt_set_sd_power(priv, WMT_SD_POWER_OFF); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (ios->clock != 0) 68562306a36Sopenharmony_ci clk_set_rate(priv->clk_sdmmc, ios->clock); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci busmode = readb(priv->sdmmc_base + SDMMC_BUSMODE); 68862306a36Sopenharmony_ci extctrl = readb(priv->sdmmc_base + SDMMC_EXTCTRL); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci busmode &= ~(BM_EIGHTBIT_MODE | BM_FOURBIT_MODE); 69162306a36Sopenharmony_ci extctrl &= ~EXT_EIGHTBIT; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci switch (ios->bus_width) { 69462306a36Sopenharmony_ci case MMC_BUS_WIDTH_8: 69562306a36Sopenharmony_ci busmode |= BM_EIGHTBIT_MODE; 69662306a36Sopenharmony_ci extctrl |= EXT_EIGHTBIT; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 69962306a36Sopenharmony_ci busmode |= BM_FOURBIT_MODE; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci case MMC_BUS_WIDTH_1: 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci writeb(busmode, priv->sdmmc_base + SDMMC_BUSMODE); 70662306a36Sopenharmony_ci writeb(extctrl, priv->sdmmc_base + SDMMC_EXTCTRL); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int wmt_mci_get_ro(struct mmc_host *mmc) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct wmt_mci_priv *priv = mmc_priv(mmc); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int wmt_mci_get_cd(struct mmc_host *mmc) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct wmt_mci_priv *priv = mmc_priv(mmc); 71962306a36Sopenharmony_ci u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return !(cd ^ priv->cd_inverted); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic const struct mmc_host_ops wmt_mci_ops = { 72562306a36Sopenharmony_ci .request = wmt_mci_request, 72662306a36Sopenharmony_ci .set_ios = wmt_mci_set_ios, 72762306a36Sopenharmony_ci .get_ro = wmt_mci_get_ro, 72862306a36Sopenharmony_ci .get_cd = wmt_mci_get_cd, 72962306a36Sopenharmony_ci}; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* Controller capabilities */ 73262306a36Sopenharmony_cistatic struct wmt_mci_caps wm8505_caps = { 73362306a36Sopenharmony_ci .f_min = 390425, 73462306a36Sopenharmony_ci .f_max = 50000000, 73562306a36Sopenharmony_ci .ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34, 73662306a36Sopenharmony_ci .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | 73762306a36Sopenharmony_ci MMC_CAP_SD_HIGHSPEED, 73862306a36Sopenharmony_ci .max_seg_size = 65024, 73962306a36Sopenharmony_ci .max_segs = 128, 74062306a36Sopenharmony_ci .max_blk_size = 2048, 74162306a36Sopenharmony_ci}; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic const struct of_device_id wmt_mci_dt_ids[] = { 74462306a36Sopenharmony_ci { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, 74562306a36Sopenharmony_ci { /* Sentinel */ }, 74662306a36Sopenharmony_ci}; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int wmt_mci_probe(struct platform_device *pdev) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct mmc_host *mmc; 75162306a36Sopenharmony_ci struct wmt_mci_priv *priv; 75262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 75362306a36Sopenharmony_ci const struct wmt_mci_caps *wmt_caps; 75462306a36Sopenharmony_ci int ret; 75562306a36Sopenharmony_ci int regular_irq, dma_irq; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci wmt_caps = of_device_get_match_data(&pdev->dev); 75862306a36Sopenharmony_ci if (!wmt_caps) { 75962306a36Sopenharmony_ci dev_err(&pdev->dev, "Controller capabilities data missing\n"); 76062306a36Sopenharmony_ci return -EFAULT; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (!np) { 76462306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n"); 76562306a36Sopenharmony_ci return -EFAULT; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci regular_irq = irq_of_parse_and_map(np, 0); 76962306a36Sopenharmony_ci dma_irq = irq_of_parse_and_map(np, 1); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!regular_irq || !dma_irq) { 77262306a36Sopenharmony_ci dev_err(&pdev->dev, "Getting IRQs failed!\n"); 77362306a36Sopenharmony_ci ret = -ENXIO; 77462306a36Sopenharmony_ci goto fail1; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); 77862306a36Sopenharmony_ci if (!mmc) { 77962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); 78062306a36Sopenharmony_ci ret = -ENOMEM; 78162306a36Sopenharmony_ci goto fail1; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci mmc->ops = &wmt_mci_ops; 78562306a36Sopenharmony_ci mmc->f_min = wmt_caps->f_min; 78662306a36Sopenharmony_ci mmc->f_max = wmt_caps->f_max; 78762306a36Sopenharmony_ci mmc->ocr_avail = wmt_caps->ocr_avail; 78862306a36Sopenharmony_ci mmc->caps = wmt_caps->caps; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci mmc->max_seg_size = wmt_caps->max_seg_size; 79162306a36Sopenharmony_ci mmc->max_segs = wmt_caps->max_segs; 79262306a36Sopenharmony_ci mmc->max_blk_size = wmt_caps->max_blk_size; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci mmc->max_req_size = (16*512*mmc->max_segs); 79562306a36Sopenharmony_ci mmc->max_blk_count = mmc->max_req_size / 512; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci priv = mmc_priv(mmc); 79862306a36Sopenharmony_ci priv->mmc = mmc; 79962306a36Sopenharmony_ci priv->dev = &pdev->dev; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci priv->power_inverted = 0; 80262306a36Sopenharmony_ci priv->cd_inverted = 0; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci priv->power_inverted = of_property_read_bool(np, "sdon-inverted"); 80562306a36Sopenharmony_ci priv->cd_inverted = of_property_read_bool(np, "cd-inverted"); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci priv->sdmmc_base = of_iomap(np, 0); 80862306a36Sopenharmony_ci if (!priv->sdmmc_base) { 80962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map IO space\n"); 81062306a36Sopenharmony_ci ret = -ENOMEM; 81162306a36Sopenharmony_ci goto fail2; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci priv->irq_regular = regular_irq; 81562306a36Sopenharmony_ci priv->irq_dma = dma_irq; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv); 81862306a36Sopenharmony_ci if (ret) { 81962306a36Sopenharmony_ci dev_err(&pdev->dev, "Register regular IRQ fail\n"); 82062306a36Sopenharmony_ci goto fail3; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci ret = request_irq(dma_irq, wmt_mci_dma_isr, 0, "sdmmc", priv); 82462306a36Sopenharmony_ci if (ret) { 82562306a36Sopenharmony_ci dev_err(&pdev->dev, "Register DMA IRQ fail\n"); 82662306a36Sopenharmony_ci goto fail4; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* alloc some DMA buffers for descriptors/transfers */ 83062306a36Sopenharmony_ci priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev, 83162306a36Sopenharmony_ci mmc->max_blk_count * 16, 83262306a36Sopenharmony_ci &priv->dma_desc_device_addr, 83362306a36Sopenharmony_ci GFP_KERNEL); 83462306a36Sopenharmony_ci if (!priv->dma_desc_buffer) { 83562306a36Sopenharmony_ci dev_err(&pdev->dev, "DMA alloc fail\n"); 83662306a36Sopenharmony_ci ret = -EPERM; 83762306a36Sopenharmony_ci goto fail5; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci platform_set_drvdata(pdev, mmc); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci priv->clk_sdmmc = of_clk_get(np, 0); 84362306a36Sopenharmony_ci if (IS_ERR(priv->clk_sdmmc)) { 84462306a36Sopenharmony_ci dev_err(&pdev->dev, "Error getting clock\n"); 84562306a36Sopenharmony_ci ret = PTR_ERR(priv->clk_sdmmc); 84662306a36Sopenharmony_ci goto fail5_and_a_half; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci ret = clk_prepare_enable(priv->clk_sdmmc); 85062306a36Sopenharmony_ci if (ret) 85162306a36Sopenharmony_ci goto fail6; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* configure the controller to a known 'ready' state */ 85462306a36Sopenharmony_ci wmt_reset_hardware(mmc); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = mmc_add_host(mmc); 85762306a36Sopenharmony_ci if (ret) 85862306a36Sopenharmony_ci goto fail7; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci dev_info(&pdev->dev, "WMT SDHC Controller initialized\n"); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_cifail7: 86462306a36Sopenharmony_ci clk_disable_unprepare(priv->clk_sdmmc); 86562306a36Sopenharmony_cifail6: 86662306a36Sopenharmony_ci clk_put(priv->clk_sdmmc); 86762306a36Sopenharmony_cifail5_and_a_half: 86862306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, mmc->max_blk_count * 16, 86962306a36Sopenharmony_ci priv->dma_desc_buffer, priv->dma_desc_device_addr); 87062306a36Sopenharmony_cifail5: 87162306a36Sopenharmony_ci free_irq(dma_irq, priv); 87262306a36Sopenharmony_cifail4: 87362306a36Sopenharmony_ci free_irq(regular_irq, priv); 87462306a36Sopenharmony_cifail3: 87562306a36Sopenharmony_ci iounmap(priv->sdmmc_base); 87662306a36Sopenharmony_cifail2: 87762306a36Sopenharmony_ci mmc_free_host(mmc); 87862306a36Sopenharmony_cifail1: 87962306a36Sopenharmony_ci return ret; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic void wmt_mci_remove(struct platform_device *pdev) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct mmc_host *mmc; 88562306a36Sopenharmony_ci struct wmt_mci_priv *priv; 88662306a36Sopenharmony_ci u32 reg_tmp; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci mmc = platform_get_drvdata(pdev); 88962306a36Sopenharmony_ci priv = mmc_priv(mmc); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* reset SD controller */ 89262306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); 89362306a36Sopenharmony_ci writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); 89462306a36Sopenharmony_ci reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); 89562306a36Sopenharmony_ci writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN); 89662306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); 89762306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* release the dma buffers */ 90062306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16, 90162306a36Sopenharmony_ci priv->dma_desc_buffer, priv->dma_desc_device_addr); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci mmc_remove_host(mmc); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci free_irq(priv->irq_regular, priv); 90662306a36Sopenharmony_ci free_irq(priv->irq_dma, priv); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci iounmap(priv->sdmmc_base); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci clk_disable_unprepare(priv->clk_sdmmc); 91162306a36Sopenharmony_ci clk_put(priv->clk_sdmmc); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci mmc_free_host(mmc); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci dev_info(&pdev->dev, "WMT MCI device removed\n"); 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci#ifdef CONFIG_PM 91962306a36Sopenharmony_cistatic int wmt_mci_suspend(struct device *dev) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci u32 reg_tmp; 92262306a36Sopenharmony_ci struct mmc_host *mmc = dev_get_drvdata(dev); 92362306a36Sopenharmony_ci struct wmt_mci_priv *priv; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (!mmc) 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci priv = mmc_priv(mmc); 92962306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); 93062306a36Sopenharmony_ci writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + 93162306a36Sopenharmony_ci SDMMC_BUSMODE); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); 93462306a36Sopenharmony_ci writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); 93762306a36Sopenharmony_ci writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci clk_disable(priv->clk_sdmmc); 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int wmt_mci_resume(struct device *dev) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci u32 reg_tmp; 94662306a36Sopenharmony_ci struct mmc_host *mmc = dev_get_drvdata(dev); 94762306a36Sopenharmony_ci struct wmt_mci_priv *priv; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (mmc) { 95062306a36Sopenharmony_ci priv = mmc_priv(mmc); 95162306a36Sopenharmony_ci clk_enable(priv->clk_sdmmc); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); 95462306a36Sopenharmony_ci writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + 95562306a36Sopenharmony_ci SDMMC_BUSMODE); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); 95862306a36Sopenharmony_ci writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE), 95962306a36Sopenharmony_ci priv->sdmmc_base + SDMMC_BLKLEN); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); 96262306a36Sopenharmony_ci writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + 96362306a36Sopenharmony_ci SDMMC_INTMASK0); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic const struct dev_pm_ops wmt_mci_pm = { 97162306a36Sopenharmony_ci .suspend = wmt_mci_suspend, 97262306a36Sopenharmony_ci .resume = wmt_mci_resume, 97362306a36Sopenharmony_ci}; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci#define wmt_mci_pm_ops (&wmt_mci_pm) 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci#else /* !CONFIG_PM */ 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci#define wmt_mci_pm_ops NULL 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci#endif 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic struct platform_driver wmt_mci_driver = { 98462306a36Sopenharmony_ci .probe = wmt_mci_probe, 98562306a36Sopenharmony_ci .remove_new = wmt_mci_remove, 98662306a36Sopenharmony_ci .driver = { 98762306a36Sopenharmony_ci .name = DRIVER_NAME, 98862306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 98962306a36Sopenharmony_ci .pm = wmt_mci_pm_ops, 99062306a36Sopenharmony_ci .of_match_table = wmt_mci_dt_ids, 99162306a36Sopenharmony_ci }, 99262306a36Sopenharmony_ci}; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cimodule_platform_driver(wmt_mci_driver); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ciMODULE_DESCRIPTION("Wondermedia MMC/SD Driver"); 99762306a36Sopenharmony_ciMODULE_AUTHOR("Tony Prisk"); 99862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 99962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); 1000