162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 462306a36Sopenharmony_ci * Copyright (C) 2013, Imagination Technologies 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * JZ4740 SD/MMC controller driver 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/dmaengine.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/irq.h> 1862306a36Sopenharmony_ci#include <linux/mmc/host.h> 1962306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/of_device.h> 2262306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2562306a36Sopenharmony_ci#include <linux/scatterlist.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/cacheflush.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define JZ_REG_MMC_STRPCL 0x00 3062306a36Sopenharmony_ci#define JZ_REG_MMC_STATUS 0x04 3162306a36Sopenharmony_ci#define JZ_REG_MMC_CLKRT 0x08 3262306a36Sopenharmony_ci#define JZ_REG_MMC_CMDAT 0x0C 3362306a36Sopenharmony_ci#define JZ_REG_MMC_RESTO 0x10 3462306a36Sopenharmony_ci#define JZ_REG_MMC_RDTO 0x14 3562306a36Sopenharmony_ci#define JZ_REG_MMC_BLKLEN 0x18 3662306a36Sopenharmony_ci#define JZ_REG_MMC_NOB 0x1C 3762306a36Sopenharmony_ci#define JZ_REG_MMC_SNOB 0x20 3862306a36Sopenharmony_ci#define JZ_REG_MMC_IMASK 0x24 3962306a36Sopenharmony_ci#define JZ_REG_MMC_IREG 0x28 4062306a36Sopenharmony_ci#define JZ_REG_MMC_CMD 0x2C 4162306a36Sopenharmony_ci#define JZ_REG_MMC_ARG 0x30 4262306a36Sopenharmony_ci#define JZ_REG_MMC_RESP_FIFO 0x34 4362306a36Sopenharmony_ci#define JZ_REG_MMC_RXFIFO 0x38 4462306a36Sopenharmony_ci#define JZ_REG_MMC_TXFIFO 0x3C 4562306a36Sopenharmony_ci#define JZ_REG_MMC_LPM 0x40 4662306a36Sopenharmony_ci#define JZ_REG_MMC_DMAC 0x44 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) 4962306a36Sopenharmony_ci#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) 5062306a36Sopenharmony_ci#define JZ_MMC_STRPCL_START_READWAIT BIT(5) 5162306a36Sopenharmony_ci#define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) 5262306a36Sopenharmony_ci#define JZ_MMC_STRPCL_RESET BIT(3) 5362306a36Sopenharmony_ci#define JZ_MMC_STRPCL_START_OP BIT(2) 5462306a36Sopenharmony_ci#define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) 5562306a36Sopenharmony_ci#define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) 5662306a36Sopenharmony_ci#define JZ_MMC_STRPCL_CLOCK_START BIT(1) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define JZ_MMC_STATUS_IS_RESETTING BIT(15) 6062306a36Sopenharmony_ci#define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) 6162306a36Sopenharmony_ci#define JZ_MMC_STATUS_PRG_DONE BIT(13) 6262306a36Sopenharmony_ci#define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) 6362306a36Sopenharmony_ci#define JZ_MMC_STATUS_END_CMD_RES BIT(11) 6462306a36Sopenharmony_ci#define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) 6562306a36Sopenharmony_ci#define JZ_MMC_STATUS_IS_READWAIT BIT(9) 6662306a36Sopenharmony_ci#define JZ_MMC_STATUS_CLK_EN BIT(8) 6762306a36Sopenharmony_ci#define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) 6862306a36Sopenharmony_ci#define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) 6962306a36Sopenharmony_ci#define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) 7062306a36Sopenharmony_ci#define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) 7162306a36Sopenharmony_ci#define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) 7262306a36Sopenharmony_ci#define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) 7362306a36Sopenharmony_ci#define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) 7462306a36Sopenharmony_ci#define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) 7762306a36Sopenharmony_ci#define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define JZ_MMC_CMDAT_IO_ABORT BIT(11) 8162306a36Sopenharmony_ci#define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) 8262306a36Sopenharmony_ci#define JZ_MMC_CMDAT_BUS_WIDTH_8BIT (BIT(10) | BIT(9)) 8362306a36Sopenharmony_ci#define JZ_MMC_CMDAT_BUS_WIDTH_MASK (BIT(10) | BIT(9)) 8462306a36Sopenharmony_ci#define JZ_MMC_CMDAT_DMA_EN BIT(8) 8562306a36Sopenharmony_ci#define JZ_MMC_CMDAT_INIT BIT(7) 8662306a36Sopenharmony_ci#define JZ_MMC_CMDAT_BUSY BIT(6) 8762306a36Sopenharmony_ci#define JZ_MMC_CMDAT_STREAM BIT(5) 8862306a36Sopenharmony_ci#define JZ_MMC_CMDAT_WRITE BIT(4) 8962306a36Sopenharmony_ci#define JZ_MMC_CMDAT_DATA_EN BIT(3) 9062306a36Sopenharmony_ci#define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) 9162306a36Sopenharmony_ci#define JZ_MMC_CMDAT_RSP_R1 1 9262306a36Sopenharmony_ci#define JZ_MMC_CMDAT_RSP_R2 2 9362306a36Sopenharmony_ci#define JZ_MMC_CMDAT_RSP_R3 3 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define JZ_MMC_IRQ_SDIO BIT(7) 9662306a36Sopenharmony_ci#define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) 9762306a36Sopenharmony_ci#define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) 9862306a36Sopenharmony_ci#define JZ_MMC_IRQ_END_CMD_RES BIT(2) 9962306a36Sopenharmony_ci#define JZ_MMC_IRQ_PRG_DONE BIT(1) 10062306a36Sopenharmony_ci#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define JZ_MMC_DMAC_DMA_SEL BIT(1) 10362306a36Sopenharmony_ci#define JZ_MMC_DMAC_DMA_EN BIT(0) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define JZ_MMC_LPM_DRV_RISING BIT(31) 10662306a36Sopenharmony_ci#define JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY BIT(31) 10762306a36Sopenharmony_ci#define JZ_MMC_LPM_DRV_RISING_1NS_DLY BIT(30) 10862306a36Sopenharmony_ci#define JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY BIT(29) 10962306a36Sopenharmony_ci#define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define JZ_MMC_CLK_RATE 24000000 11262306a36Sopenharmony_ci#define JZ_MMC_REQ_TIMEOUT_MS 5000 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cienum jz4740_mmc_version { 11562306a36Sopenharmony_ci JZ_MMC_JZ4740, 11662306a36Sopenharmony_ci JZ_MMC_JZ4725B, 11762306a36Sopenharmony_ci JZ_MMC_JZ4760, 11862306a36Sopenharmony_ci JZ_MMC_JZ4780, 11962306a36Sopenharmony_ci JZ_MMC_X1000, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cienum jz4740_mmc_state { 12362306a36Sopenharmony_ci JZ4740_MMC_STATE_READ_RESPONSE, 12462306a36Sopenharmony_ci JZ4740_MMC_STATE_TRANSFER_DATA, 12562306a36Sopenharmony_ci JZ4740_MMC_STATE_SEND_STOP, 12662306a36Sopenharmony_ci JZ4740_MMC_STATE_DONE, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* 13062306a36Sopenharmony_ci * The MMC core allows to prepare a mmc_request while another mmc_request 13162306a36Sopenharmony_ci * is in-flight. This is used via the pre_req/post_req hooks. 13262306a36Sopenharmony_ci * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request. 13362306a36Sopenharmony_ci * Following what other drivers do (sdhci, dw_mmc) we use the following cookie 13462306a36Sopenharmony_ci * flags to keep track of the mmc_request mapping state. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * COOKIE_UNMAPPED: the request is not mapped. 13762306a36Sopenharmony_ci * COOKIE_PREMAPPED: the request was mapped in pre_req, 13862306a36Sopenharmony_ci * and should be unmapped in post_req. 13962306a36Sopenharmony_ci * COOKIE_MAPPED: the request was mapped in the irq handler, 14062306a36Sopenharmony_ci * and should be unmapped before mmc_request_done is called.. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cienum jz4780_cookie { 14362306a36Sopenharmony_ci COOKIE_UNMAPPED = 0, 14462306a36Sopenharmony_ci COOKIE_PREMAPPED, 14562306a36Sopenharmony_ci COOKIE_MAPPED, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct jz4740_mmc_host { 14962306a36Sopenharmony_ci struct mmc_host *mmc; 15062306a36Sopenharmony_ci struct platform_device *pdev; 15162306a36Sopenharmony_ci struct clk *clk; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci enum jz4740_mmc_version version; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci int irq; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci void __iomem *base; 15862306a36Sopenharmony_ci struct resource *mem_res; 15962306a36Sopenharmony_ci struct mmc_request *req; 16062306a36Sopenharmony_ci struct mmc_command *cmd; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci bool vqmmc_enabled; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci unsigned long waiting; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci uint32_t cmdat; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci uint32_t irq_mask; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci spinlock_t lock; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci struct timer_list timeout_timer; 17362306a36Sopenharmony_ci struct sg_mapping_iter miter; 17462306a36Sopenharmony_ci enum jz4740_mmc_state state; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* DMA support */ 17762306a36Sopenharmony_ci struct dma_chan *dma_rx; 17862306a36Sopenharmony_ci struct dma_chan *dma_tx; 17962306a36Sopenharmony_ci bool use_dma; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* The DMA trigger level is 8 words, that is to say, the DMA read 18262306a36Sopenharmony_ci * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write 18362306a36Sopenharmony_ci * trigger is when data words in MSC_TXFIFO is < 8. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci#define JZ4740_MMC_FIFO_HALF_SIZE 8 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host, 18962306a36Sopenharmony_ci uint32_t val) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci if (host->version >= JZ_MMC_JZ4725B) 19262306a36Sopenharmony_ci return writel(val, host->base + JZ_REG_MMC_IMASK); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci return writew(val, host->base + JZ_REG_MMC_IMASK); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host, 19862306a36Sopenharmony_ci uint32_t val) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) 20162306a36Sopenharmony_ci writel(val, host->base + JZ_REG_MMC_IREG); 20262306a36Sopenharmony_ci else 20362306a36Sopenharmony_ci writew(val, host->base + JZ_REG_MMC_IREG); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) 20962306a36Sopenharmony_ci return readl(host->base + JZ_REG_MMC_IREG); 21062306a36Sopenharmony_ci else 21162306a36Sopenharmony_ci return readw(host->base + JZ_REG_MMC_IREG); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 21562306a36Sopenharmony_ci/* DMA infrastructure */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (!host->use_dma) 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dma_release_channel(host->dma_tx); 22362306a36Sopenharmony_ci if (host->dma_rx) 22462306a36Sopenharmony_ci dma_release_channel(host->dma_rx); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct device *dev = mmc_dev(host->mmc); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci host->dma_tx = dma_request_chan(dev, "tx-rx"); 23262306a36Sopenharmony_ci if (!IS_ERR(host->dma_tx)) 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (PTR_ERR(host->dma_tx) != -ENODEV) { 23662306a36Sopenharmony_ci dev_err(dev, "Failed to get dma tx-rx channel\n"); 23762306a36Sopenharmony_ci return PTR_ERR(host->dma_tx); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); 24162306a36Sopenharmony_ci if (IS_ERR(host->dma_tx)) { 24262306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); 24362306a36Sopenharmony_ci return PTR_ERR(host->dma_tx); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); 24762306a36Sopenharmony_ci if (IS_ERR(host->dma_rx)) { 24862306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); 24962306a36Sopenharmony_ci dma_release_channel(host->dma_tx); 25062306a36Sopenharmony_ci return PTR_ERR(host->dma_rx); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * Limit the maximum segment size in any SG entry according to 25562306a36Sopenharmony_ci * the parameters of the DMA engine device. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci if (host->dma_tx) { 25862306a36Sopenharmony_ci struct device *dev = host->dma_tx->device->dev; 25962306a36Sopenharmony_ci unsigned int max_seg_size = dma_get_max_seg_size(dev); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (max_seg_size < host->mmc->max_seg_size) 26262306a36Sopenharmony_ci host->mmc->max_seg_size = max_seg_size; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (host->dma_rx) { 26662306a36Sopenharmony_ci struct device *dev = host->dma_rx->device->dev; 26762306a36Sopenharmony_ci unsigned int max_seg_size = dma_get_max_seg_size(dev); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (max_seg_size < host->mmc->max_seg_size) 27062306a36Sopenharmony_ci host->mmc->max_seg_size = max_seg_size; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, 27762306a36Sopenharmony_ci struct mmc_data *data) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci if ((data->flags & MMC_DATA_READ) && host->dma_rx) 28062306a36Sopenharmony_ci return host->dma_rx; 28162306a36Sopenharmony_ci else 28262306a36Sopenharmony_ci return host->dma_tx; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, 28662306a36Sopenharmony_ci struct mmc_data *data) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 28962306a36Sopenharmony_ci enum dma_data_direction dir = mmc_get_dma_dir(data); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); 29262306a36Sopenharmony_ci data->host_cookie = COOKIE_UNMAPPED; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* Prepares DMA data for current or next transfer. 29662306a36Sopenharmony_ci * A request can be in-flight when this is called. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_cistatic int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, 29962306a36Sopenharmony_ci struct mmc_data *data, 30062306a36Sopenharmony_ci int cookie) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 30362306a36Sopenharmony_ci enum dma_data_direction dir = mmc_get_dma_dir(data); 30462306a36Sopenharmony_ci unsigned int sg_count; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (data->host_cookie == COOKIE_PREMAPPED) 30762306a36Sopenharmony_ci return data->sg_count; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci sg_count = dma_map_sg(chan->device->dev, 31062306a36Sopenharmony_ci data->sg, 31162306a36Sopenharmony_ci data->sg_len, 31262306a36Sopenharmony_ci dir); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!sg_count) { 31562306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 31662306a36Sopenharmony_ci "Failed to map scatterlist for DMA operation\n"); 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci data->sg_count = sg_count; 32162306a36Sopenharmony_ci data->host_cookie = cookie; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return data->sg_count; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, 32762306a36Sopenharmony_ci struct mmc_data *data) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 33062306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 33162306a36Sopenharmony_ci struct dma_slave_config conf = { 33262306a36Sopenharmony_ci .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 33362306a36Sopenharmony_ci .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 33462306a36Sopenharmony_ci .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 33562306a36Sopenharmony_ci .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 33662306a36Sopenharmony_ci }; 33762306a36Sopenharmony_ci int sg_count; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) { 34062306a36Sopenharmony_ci conf.direction = DMA_MEM_TO_DEV; 34162306a36Sopenharmony_ci conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; 34262306a36Sopenharmony_ci } else { 34362306a36Sopenharmony_ci conf.direction = DMA_DEV_TO_MEM; 34462306a36Sopenharmony_ci conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED); 34862306a36Sopenharmony_ci if (sg_count < 0) 34962306a36Sopenharmony_ci return sg_count; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci dmaengine_slave_config(chan, &conf); 35262306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count, 35362306a36Sopenharmony_ci conf.direction, 35462306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 35562306a36Sopenharmony_ci if (!desc) { 35662306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 35762306a36Sopenharmony_ci "Failed to allocate DMA %s descriptor", 35862306a36Sopenharmony_ci conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX"); 35962306a36Sopenharmony_ci goto dma_unmap; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci dmaengine_submit(desc); 36362306a36Sopenharmony_ci dma_async_issue_pending(chan); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cidma_unmap: 36862306a36Sopenharmony_ci if (data->host_cookie == COOKIE_MAPPED) 36962306a36Sopenharmony_ci jz4740_mmc_dma_unmap(host, data); 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void jz4740_mmc_pre_request(struct mmc_host *mmc, 37462306a36Sopenharmony_ci struct mmc_request *mrq) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 37762306a36Sopenharmony_ci struct mmc_data *data = mrq->data; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!host->use_dma) 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci data->host_cookie = COOKIE_UNMAPPED; 38362306a36Sopenharmony_ci if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0) 38462306a36Sopenharmony_ci data->host_cookie = COOKIE_UNMAPPED; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void jz4740_mmc_post_request(struct mmc_host *mmc, 38862306a36Sopenharmony_ci struct mmc_request *mrq, 38962306a36Sopenharmony_ci int err) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 39262306a36Sopenharmony_ci struct mmc_data *data = mrq->data; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (data && data->host_cookie != COOKIE_UNMAPPED) 39562306a36Sopenharmony_ci jz4740_mmc_dma_unmap(host, data); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (err) { 39862306a36Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci dmaengine_terminate_all(chan); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 40762306a36Sopenharmony_ci unsigned int irq, bool enabled) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci unsigned long flags; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 41262306a36Sopenharmony_ci if (enabled) 41362306a36Sopenharmony_ci host->irq_mask &= ~irq; 41462306a36Sopenharmony_ci else 41562306a36Sopenharmony_ci host->irq_mask |= irq; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci jz4740_mmc_write_irq_mask(host, host->irq_mask); 41862306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, 42262306a36Sopenharmony_ci bool start_transfer) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci uint16_t val = JZ_MMC_STRPCL_CLOCK_START; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (start_transfer) 42762306a36Sopenharmony_ci val |= JZ_MMC_STRPCL_START_OP; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci writew(val, host->base + JZ_REG_MMC_STRPCL); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci uint32_t status; 43562306a36Sopenharmony_ci unsigned int timeout = 1000; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); 43862306a36Sopenharmony_ci do { 43962306a36Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 44062306a36Sopenharmony_ci } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void jz4740_mmc_reset(struct jz4740_mmc_host *host) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci uint32_t status; 44662306a36Sopenharmony_ci unsigned int timeout = 1000; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); 44962306a36Sopenharmony_ci udelay(10); 45062306a36Sopenharmony_ci do { 45162306a36Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 45262306a36Sopenharmony_ci } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void jz4740_mmc_request_done(struct jz4740_mmc_host *host) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct mmc_request *req; 45862306a36Sopenharmony_ci struct mmc_data *data; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci req = host->req; 46162306a36Sopenharmony_ci data = req->data; 46262306a36Sopenharmony_ci host->req = NULL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (data && data->host_cookie == COOKIE_MAPPED) 46562306a36Sopenharmony_ci jz4740_mmc_dma_unmap(host, data); 46662306a36Sopenharmony_ci mmc_request_done(host->mmc, req); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, 47062306a36Sopenharmony_ci unsigned int irq) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci unsigned int timeout = 0x800; 47362306a36Sopenharmony_ci uint32_t status; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci do { 47662306a36Sopenharmony_ci status = jz4740_mmc_read_irq_reg(host); 47762306a36Sopenharmony_ci } while (!(status & irq) && --timeout); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (timeout == 0) { 48062306a36Sopenharmony_ci set_bit(0, &host->waiting); 48162306a36Sopenharmony_ci mod_timer(&host->timeout_timer, 48262306a36Sopenharmony_ci jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); 48362306a36Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, irq, true); 48462306a36Sopenharmony_ci return true; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return false; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, 49162306a36Sopenharmony_ci struct mmc_data *data) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int status; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 49662306a36Sopenharmony_ci if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { 49762306a36Sopenharmony_ci if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { 49862306a36Sopenharmony_ci host->req->cmd->error = -ETIMEDOUT; 49962306a36Sopenharmony_ci data->error = -ETIMEDOUT; 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci host->req->cmd->error = -EIO; 50262306a36Sopenharmony_ci data->error = -EIO; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { 50562306a36Sopenharmony_ci if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) { 50662306a36Sopenharmony_ci host->req->cmd->error = -ETIMEDOUT; 50762306a36Sopenharmony_ci data->error = -ETIMEDOUT; 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci host->req->cmd->error = -EIO; 51062306a36Sopenharmony_ci data->error = -EIO; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, 51662306a36Sopenharmony_ci struct mmc_data *data) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct sg_mapping_iter *miter = &host->miter; 51962306a36Sopenharmony_ci void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; 52062306a36Sopenharmony_ci uint32_t *buf; 52162306a36Sopenharmony_ci bool timeout; 52262306a36Sopenharmony_ci size_t i, j; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci while (sg_miter_next(miter)) { 52562306a36Sopenharmony_ci buf = miter->addr; 52662306a36Sopenharmony_ci i = miter->length / 4; 52762306a36Sopenharmony_ci j = i / 8; 52862306a36Sopenharmony_ci i = i & 0x7; 52962306a36Sopenharmony_ci while (j) { 53062306a36Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 53162306a36Sopenharmony_ci if (unlikely(timeout)) 53262306a36Sopenharmony_ci goto poll_timeout; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci writel(buf[0], fifo_addr); 53562306a36Sopenharmony_ci writel(buf[1], fifo_addr); 53662306a36Sopenharmony_ci writel(buf[2], fifo_addr); 53762306a36Sopenharmony_ci writel(buf[3], fifo_addr); 53862306a36Sopenharmony_ci writel(buf[4], fifo_addr); 53962306a36Sopenharmony_ci writel(buf[5], fifo_addr); 54062306a36Sopenharmony_ci writel(buf[6], fifo_addr); 54162306a36Sopenharmony_ci writel(buf[7], fifo_addr); 54262306a36Sopenharmony_ci buf += 8; 54362306a36Sopenharmony_ci --j; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci if (unlikely(i)) { 54662306a36Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 54762306a36Sopenharmony_ci if (unlikely(timeout)) 54862306a36Sopenharmony_ci goto poll_timeout; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci while (i) { 55162306a36Sopenharmony_ci writel(*buf, fifo_addr); 55262306a36Sopenharmony_ci ++buf; 55362306a36Sopenharmony_ci --i; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci data->bytes_xfered += miter->length; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci sg_miter_stop(miter); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return false; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cipoll_timeout: 56362306a36Sopenharmony_ci miter->consumed = (void *)buf - miter->addr; 56462306a36Sopenharmony_ci data->bytes_xfered += miter->consumed; 56562306a36Sopenharmony_ci sg_miter_stop(miter); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return true; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, 57162306a36Sopenharmony_ci struct mmc_data *data) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct sg_mapping_iter *miter = &host->miter; 57462306a36Sopenharmony_ci void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; 57562306a36Sopenharmony_ci uint32_t *buf; 57662306a36Sopenharmony_ci uint32_t d; 57762306a36Sopenharmony_ci uint32_t status; 57862306a36Sopenharmony_ci size_t i, j; 57962306a36Sopenharmony_ci unsigned int timeout; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci while (sg_miter_next(miter)) { 58262306a36Sopenharmony_ci buf = miter->addr; 58362306a36Sopenharmony_ci i = miter->length; 58462306a36Sopenharmony_ci j = i / 32; 58562306a36Sopenharmony_ci i = i & 0x1f; 58662306a36Sopenharmony_ci while (j) { 58762306a36Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 58862306a36Sopenharmony_ci if (unlikely(timeout)) 58962306a36Sopenharmony_ci goto poll_timeout; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci buf[0] = readl(fifo_addr); 59262306a36Sopenharmony_ci buf[1] = readl(fifo_addr); 59362306a36Sopenharmony_ci buf[2] = readl(fifo_addr); 59462306a36Sopenharmony_ci buf[3] = readl(fifo_addr); 59562306a36Sopenharmony_ci buf[4] = readl(fifo_addr); 59662306a36Sopenharmony_ci buf[5] = readl(fifo_addr); 59762306a36Sopenharmony_ci buf[6] = readl(fifo_addr); 59862306a36Sopenharmony_ci buf[7] = readl(fifo_addr); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci buf += 8; 60162306a36Sopenharmony_ci --j; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (unlikely(i)) { 60562306a36Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 60662306a36Sopenharmony_ci if (unlikely(timeout)) 60762306a36Sopenharmony_ci goto poll_timeout; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci while (i >= 4) { 61062306a36Sopenharmony_ci *buf++ = readl(fifo_addr); 61162306a36Sopenharmony_ci i -= 4; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci if (unlikely(i > 0)) { 61462306a36Sopenharmony_ci d = readl(fifo_addr); 61562306a36Sopenharmony_ci memcpy(buf, &d, i); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci data->bytes_xfered += miter->length; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci sg_miter_stop(miter); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* For whatever reason there is sometime one word more in the fifo then 62362306a36Sopenharmony_ci * requested */ 62462306a36Sopenharmony_ci timeout = 1000; 62562306a36Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 62662306a36Sopenharmony_ci while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { 62762306a36Sopenharmony_ci d = readl(fifo_addr); 62862306a36Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return false; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cipoll_timeout: 63462306a36Sopenharmony_ci miter->consumed = (void *)buf - miter->addr; 63562306a36Sopenharmony_ci data->bytes_xfered += miter->consumed; 63662306a36Sopenharmony_ci sg_miter_stop(miter); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return true; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic void jz4740_mmc_timeout(struct timer_list *t) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!test_and_clear_bit(0, &host->waiting)) 64662306a36Sopenharmony_ci return; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci host->req->cmd->error = -ETIMEDOUT; 65162306a36Sopenharmony_ci jz4740_mmc_request_done(host); 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic void jz4740_mmc_read_response(struct jz4740_mmc_host *host, 65562306a36Sopenharmony_ci struct mmc_command *cmd) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci int i; 65862306a36Sopenharmony_ci uint16_t tmp; 65962306a36Sopenharmony_ci void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_136) { 66262306a36Sopenharmony_ci tmp = readw(fifo_addr); 66362306a36Sopenharmony_ci for (i = 0; i < 4; ++i) { 66462306a36Sopenharmony_ci cmd->resp[i] = tmp << 24; 66562306a36Sopenharmony_ci tmp = readw(fifo_addr); 66662306a36Sopenharmony_ci cmd->resp[i] |= tmp << 8; 66762306a36Sopenharmony_ci tmp = readw(fifo_addr); 66862306a36Sopenharmony_ci cmd->resp[i] |= tmp >> 8; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci } else { 67162306a36Sopenharmony_ci cmd->resp[0] = readw(fifo_addr) << 24; 67262306a36Sopenharmony_ci cmd->resp[0] |= readw(fifo_addr) << 8; 67362306a36Sopenharmony_ci cmd->resp[0] |= readw(fifo_addr) & 0xff; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic void jz4740_mmc_send_command(struct jz4740_mmc_host *host, 67862306a36Sopenharmony_ci struct mmc_command *cmd) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci uint32_t cmdat = host->cmdat; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_INIT; 68362306a36Sopenharmony_ci jz4740_mmc_clock_disable(host); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci host->cmd = cmd; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_BUSY) 68862306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_BUSY; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci switch (mmc_resp_type(cmd)) { 69162306a36Sopenharmony_ci case MMC_RSP_R1B: 69262306a36Sopenharmony_ci case MMC_RSP_R1: 69362306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_RSP_R1; 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci case MMC_RSP_R2: 69662306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_RSP_R2; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci case MMC_RSP_R3: 69962306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_RSP_R3; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci default: 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (cmd->data) { 70662306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_DATA_EN; 70762306a36Sopenharmony_ci if (cmd->data->flags & MMC_DATA_WRITE) 70862306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_WRITE; 70962306a36Sopenharmony_ci if (host->use_dma) { 71062306a36Sopenharmony_ci /* 71162306a36Sopenharmony_ci * The JZ4780's MMC controller has integrated DMA ability 71262306a36Sopenharmony_ci * in addition to being able to use the external DMA 71362306a36Sopenharmony_ci * controller. It moves DMA control bits to a separate 71462306a36Sopenharmony_ci * register. The DMA_SEL bit chooses the external 71562306a36Sopenharmony_ci * controller over the integrated one. Earlier SoCs 71662306a36Sopenharmony_ci * can only use the external controller, and have a 71762306a36Sopenharmony_ci * single DMA enable bit in CMDAT. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) { 72062306a36Sopenharmony_ci writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL, 72162306a36Sopenharmony_ci host->base + JZ_REG_MMC_DMAC); 72262306a36Sopenharmony_ci } else { 72362306a36Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_DMA_EN; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } else if (host->version >= JZ_MMC_JZ4780) { 72662306a36Sopenharmony_ci writel(0, host->base + JZ_REG_MMC_DMAC); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); 73062306a36Sopenharmony_ci writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); 73462306a36Sopenharmony_ci writel(cmd->arg, host->base + JZ_REG_MMC_ARG); 73562306a36Sopenharmony_ci writel(cmdat, host->base + JZ_REG_MMC_CMDAT); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci jz4740_mmc_clock_enable(host, 1); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct mmc_command *cmd = host->req->cmd; 74362306a36Sopenharmony_ci struct mmc_data *data = cmd->data; 74462306a36Sopenharmony_ci int direction; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) 74762306a36Sopenharmony_ci direction = SG_MITER_TO_SG; 74862306a36Sopenharmony_ci else 74962306a36Sopenharmony_ci direction = SG_MITER_FROM_SG; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci sg_miter_start(&host->miter, data->sg, data->sg_len, direction); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic irqreturn_t jz_mmc_irq_worker(int irq, void *devid) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; 75862306a36Sopenharmony_ci struct mmc_command *cmd = host->req->cmd; 75962306a36Sopenharmony_ci struct mmc_request *req = host->req; 76062306a36Sopenharmony_ci struct mmc_data *data = cmd->data; 76162306a36Sopenharmony_ci bool timeout = false; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (cmd->error) 76462306a36Sopenharmony_ci host->state = JZ4740_MMC_STATE_DONE; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci switch (host->state) { 76762306a36Sopenharmony_ci case JZ4740_MMC_STATE_READ_RESPONSE: 76862306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_PRESENT) 76962306a36Sopenharmony_ci jz4740_mmc_read_response(host, cmd); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!data) 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci jz_mmc_prepare_data_transfer(host); 77562306a36Sopenharmony_ci fallthrough; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci case JZ4740_MMC_STATE_TRANSFER_DATA: 77862306a36Sopenharmony_ci if (host->use_dma) { 77962306a36Sopenharmony_ci /* Use DMA if enabled. 78062306a36Sopenharmony_ci * Data transfer direction is defined later by 78162306a36Sopenharmony_ci * relying on data flags in 78262306a36Sopenharmony_ci * jz4740_mmc_prepare_dma_data() and 78362306a36Sopenharmony_ci * jz4740_mmc_start_dma_transfer(). 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci timeout = jz4740_mmc_start_dma_transfer(host, data); 78662306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 78762306a36Sopenharmony_ci } else if (data->flags & MMC_DATA_READ) 78862306a36Sopenharmony_ci /* Use PIO if DMA is not enabled. 78962306a36Sopenharmony_ci * Data transfer direction was defined before 79062306a36Sopenharmony_ci * by relying on data flags in 79162306a36Sopenharmony_ci * jz_mmc_prepare_data_transfer(). 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci timeout = jz4740_mmc_read_data(host, data); 79462306a36Sopenharmony_ci else 79562306a36Sopenharmony_ci timeout = jz4740_mmc_write_data(host, data); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (unlikely(timeout)) { 79862306a36Sopenharmony_ci host->state = JZ4740_MMC_STATE_TRANSFER_DATA; 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci jz4740_mmc_transfer_check_state(host, data); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 80562306a36Sopenharmony_ci if (unlikely(timeout)) { 80662306a36Sopenharmony_ci host->state = JZ4740_MMC_STATE_SEND_STOP; 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 81062306a36Sopenharmony_ci fallthrough; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci case JZ4740_MMC_STATE_SEND_STOP: 81362306a36Sopenharmony_ci if (!req->stop) 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci jz4740_mmc_send_command(host, req->stop); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { 81962306a36Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, 82062306a36Sopenharmony_ci JZ_MMC_IRQ_PRG_DONE); 82162306a36Sopenharmony_ci if (timeout) { 82262306a36Sopenharmony_ci host->state = JZ4740_MMC_STATE_DONE; 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci fallthrough; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci case JZ4740_MMC_STATE_DONE: 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (!timeout) 83362306a36Sopenharmony_ci jz4740_mmc_request_done(host); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return IRQ_HANDLED; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic irqreturn_t jz_mmc_irq(int irq, void *devid) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct jz4740_mmc_host *host = devid; 84162306a36Sopenharmony_ci struct mmc_command *cmd = host->cmd; 84262306a36Sopenharmony_ci uint32_t irq_reg, status, tmp; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 84562306a36Sopenharmony_ci irq_reg = jz4740_mmc_read_irq_reg(host); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci tmp = irq_reg; 84862306a36Sopenharmony_ci irq_reg &= ~host->irq_mask; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | 85162306a36Sopenharmony_ci JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (tmp != irq_reg) 85462306a36Sopenharmony_ci jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (irq_reg & JZ_MMC_IRQ_SDIO) { 85762306a36Sopenharmony_ci jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO); 85862306a36Sopenharmony_ci mmc_signal_sdio_irq(host->mmc); 85962306a36Sopenharmony_ci irq_reg &= ~JZ_MMC_IRQ_SDIO; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (host->req && cmd && irq_reg) { 86362306a36Sopenharmony_ci if (test_and_clear_bit(0, &host->waiting)) { 86462306a36Sopenharmony_ci del_timer(&host->timeout_timer); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (status & JZ_MMC_STATUS_TIMEOUT_RES) { 86762306a36Sopenharmony_ci cmd->error = -ETIMEDOUT; 86862306a36Sopenharmony_ci } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { 86962306a36Sopenharmony_ci cmd->error = -EIO; 87062306a36Sopenharmony_ci } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 87162306a36Sopenharmony_ci JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 87262306a36Sopenharmony_ci if (cmd->data) 87362306a36Sopenharmony_ci cmd->data->error = -EIO; 87462306a36Sopenharmony_ci cmd->error = -EIO; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, irq_reg, false); 87862306a36Sopenharmony_ci jz4740_mmc_write_irq_reg(host, irq_reg); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci return IRQ_HANDLED; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci int div = 0; 89062306a36Sopenharmony_ci int real_rate; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci jz4740_mmc_clock_disable(host); 89362306a36Sopenharmony_ci clk_set_rate(host->clk, host->mmc->f_max); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci real_rate = clk_get_rate(host->clk); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci while (real_rate > rate && div < 7) { 89862306a36Sopenharmony_ci ++div; 89962306a36Sopenharmony_ci real_rate >>= 1; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci writew(div, host->base + JZ_REG_MMC_CLKRT); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (real_rate > 25000000) { 90562306a36Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) { 90662306a36Sopenharmony_ci writel(JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY | 90762306a36Sopenharmony_ci JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY | 90862306a36Sopenharmony_ci JZ_MMC_LPM_LOW_POWER_MODE_EN, 90962306a36Sopenharmony_ci host->base + JZ_REG_MMC_LPM); 91062306a36Sopenharmony_ci } else if (host->version >= JZ_MMC_JZ4760) { 91162306a36Sopenharmony_ci writel(JZ_MMC_LPM_DRV_RISING | 91262306a36Sopenharmony_ci JZ_MMC_LPM_LOW_POWER_MODE_EN, 91362306a36Sopenharmony_ci host->base + JZ_REG_MMC_LPM); 91462306a36Sopenharmony_ci } else if (host->version >= JZ_MMC_JZ4725B) 91562306a36Sopenharmony_ci writel(JZ_MMC_LPM_LOW_POWER_MODE_EN, 91662306a36Sopenharmony_ci host->base + JZ_REG_MMC_LPM); 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return real_rate; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci host->req = req; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci jz4740_mmc_write_irq_reg(host, ~0); 92962306a36Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci host->state = JZ4740_MMC_STATE_READ_RESPONSE; 93262306a36Sopenharmony_ci set_bit(0, &host->waiting); 93362306a36Sopenharmony_ci mod_timer(&host->timeout_timer, 93462306a36Sopenharmony_ci jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); 93562306a36Sopenharmony_ci jz4740_mmc_send_command(host, req->cmd); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 94162306a36Sopenharmony_ci int ret; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (ios->clock) 94462306a36Sopenharmony_ci jz4740_mmc_set_clock_rate(host, ios->clock); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci switch (ios->power_mode) { 94762306a36Sopenharmony_ci case MMC_POWER_UP: 94862306a36Sopenharmony_ci jz4740_mmc_reset(host); 94962306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vmmc)) 95062306a36Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); 95162306a36Sopenharmony_ci host->cmdat |= JZ_MMC_CMDAT_INIT; 95262306a36Sopenharmony_ci clk_prepare_enable(host->clk); 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci case MMC_POWER_ON: 95562306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { 95662306a36Sopenharmony_ci ret = regulator_enable(mmc->supply.vqmmc); 95762306a36Sopenharmony_ci if (ret) 95862306a36Sopenharmony_ci dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n"); 95962306a36Sopenharmony_ci else 96062306a36Sopenharmony_ci host->vqmmc_enabled = true; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci break; 96362306a36Sopenharmony_ci case MMC_POWER_OFF: 96462306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vmmc)) 96562306a36Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); 96662306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { 96762306a36Sopenharmony_ci regulator_disable(mmc->supply.vqmmc); 96862306a36Sopenharmony_ci host->vqmmc_enabled = false; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci clk_disable_unprepare(host->clk); 97162306a36Sopenharmony_ci break; 97262306a36Sopenharmony_ci default: 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci switch (ios->bus_width) { 97762306a36Sopenharmony_ci case MMC_BUS_WIDTH_1: 97862306a36Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 98162306a36Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 98262306a36Sopenharmony_ci host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case MMC_BUS_WIDTH_8: 98562306a36Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 98662306a36Sopenharmony_ci host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_8BIT; 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci default: 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 99662306a36Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci int ret; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* vqmmc regulator is available */ 100462306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vqmmc)) { 100562306a36Sopenharmony_ci ret = mmc_regulator_set_vqmmc(mmc, ios); 100662306a36Sopenharmony_ci return ret < 0 ? ret : 0; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* no vqmmc regulator, assume fixed regulator at 3/3.3V */ 101062306a36Sopenharmony_ci if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) 101162306a36Sopenharmony_ci return 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci return -EINVAL; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic const struct mmc_host_ops jz4740_mmc_ops = { 101762306a36Sopenharmony_ci .request = jz4740_mmc_request, 101862306a36Sopenharmony_ci .pre_req = jz4740_mmc_pre_request, 101962306a36Sopenharmony_ci .post_req = jz4740_mmc_post_request, 102062306a36Sopenharmony_ci .set_ios = jz4740_mmc_set_ios, 102162306a36Sopenharmony_ci .get_ro = mmc_gpio_get_ro, 102262306a36Sopenharmony_ci .get_cd = mmc_gpio_get_cd, 102362306a36Sopenharmony_ci .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, 102462306a36Sopenharmony_ci .start_signal_voltage_switch = jz4740_voltage_switch, 102562306a36Sopenharmony_ci}; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic const struct of_device_id jz4740_mmc_of_match[] = { 102862306a36Sopenharmony_ci { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, 102962306a36Sopenharmony_ci { .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B }, 103062306a36Sopenharmony_ci { .compatible = "ingenic,jz4760-mmc", .data = (void *) JZ_MMC_JZ4760 }, 103162306a36Sopenharmony_ci { .compatible = "ingenic,jz4775-mmc", .data = (void *) JZ_MMC_JZ4780 }, 103262306a36Sopenharmony_ci { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, 103362306a36Sopenharmony_ci { .compatible = "ingenic,x1000-mmc", .data = (void *) JZ_MMC_X1000 }, 103462306a36Sopenharmony_ci {}, 103562306a36Sopenharmony_ci}; 103662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic int jz4740_mmc_probe(struct platform_device* pdev) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci int ret; 104162306a36Sopenharmony_ci struct mmc_host *mmc; 104262306a36Sopenharmony_ci struct jz4740_mmc_host *host; 104362306a36Sopenharmony_ci const struct of_device_id *match; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); 104662306a36Sopenharmony_ci if (!mmc) { 104762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); 104862306a36Sopenharmony_ci return -ENOMEM; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci host = mmc_priv(mmc); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci match = of_match_device(jz4740_mmc_of_match, &pdev->dev); 105462306a36Sopenharmony_ci if (match) { 105562306a36Sopenharmony_ci host->version = (enum jz4740_mmc_version)match->data; 105662306a36Sopenharmony_ci } else { 105762306a36Sopenharmony_ci /* JZ4740 should be the only one using legacy probe */ 105862306a36Sopenharmony_ci host->version = JZ_MMC_JZ4740; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci ret = mmc_of_parse(mmc); 106262306a36Sopenharmony_ci if (ret) { 106362306a36Sopenharmony_ci dev_err_probe(&pdev->dev, ret, "could not parse device properties\n"); 106462306a36Sopenharmony_ci goto err_free_host; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci mmc_regulator_get_supply(mmc); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci host->irq = platform_get_irq(pdev, 0); 107062306a36Sopenharmony_ci if (host->irq < 0) { 107162306a36Sopenharmony_ci ret = host->irq; 107262306a36Sopenharmony_ci goto err_free_host; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci host->clk = devm_clk_get(&pdev->dev, "mmc"); 107662306a36Sopenharmony_ci if (IS_ERR(host->clk)) { 107762306a36Sopenharmony_ci ret = PTR_ERR(host->clk); 107862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get mmc clock\n"); 107962306a36Sopenharmony_ci goto err_free_host; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->mem_res); 108362306a36Sopenharmony_ci if (IS_ERR(host->base)) { 108462306a36Sopenharmony_ci ret = PTR_ERR(host->base); 108562306a36Sopenharmony_ci goto err_free_host; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci mmc->ops = &jz4740_mmc_ops; 108962306a36Sopenharmony_ci if (!mmc->f_max) 109062306a36Sopenharmony_ci mmc->f_max = JZ_MMC_CLK_RATE; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* 109362306a36Sopenharmony_ci * There seems to be a problem with this driver on the JZ4760 and 109462306a36Sopenharmony_ci * JZ4760B SoCs. There, when using the maximum rate supported (50 MHz), 109562306a36Sopenharmony_ci * the communication fails with many SD cards. 109662306a36Sopenharmony_ci * Until this bug is sorted out, limit the maximum rate to 24 MHz. 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_ci if (host->version == JZ_MMC_JZ4760 && mmc->f_max > JZ_MMC_CLK_RATE) 109962306a36Sopenharmony_ci mmc->f_max = JZ_MMC_CLK_RATE; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci mmc->f_min = mmc->f_max / 128; 110262306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* 110562306a36Sopenharmony_ci * We use a fixed timeout of 5s, hence inform the core about it. A 110662306a36Sopenharmony_ci * future improvement should instead respect the cmd->busy_timeout. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci mmc->max_blk_size = (1 << 10) - 1; 111162306a36Sopenharmony_ci mmc->max_blk_count = (1 << 15) - 1; 111262306a36Sopenharmony_ci mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci mmc->max_segs = 128; 111562306a36Sopenharmony_ci mmc->max_seg_size = mmc->max_req_size; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci host->mmc = mmc; 111862306a36Sopenharmony_ci host->pdev = pdev; 111962306a36Sopenharmony_ci spin_lock_init(&host->lock); 112062306a36Sopenharmony_ci host->irq_mask = ~0; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci jz4740_mmc_reset(host); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, 112562306a36Sopenharmony_ci dev_name(&pdev->dev), host); 112662306a36Sopenharmony_ci if (ret) { 112762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 112862306a36Sopenharmony_ci goto err_free_host; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci jz4740_mmc_clock_disable(host); 113262306a36Sopenharmony_ci timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci ret = jz4740_mmc_acquire_dma_channels(host); 113562306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 113662306a36Sopenharmony_ci goto err_free_irq; 113762306a36Sopenharmony_ci host->use_dma = !ret; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 114062306a36Sopenharmony_ci ret = mmc_add_host(mmc); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (ret) { 114362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); 114462306a36Sopenharmony_ci goto err_release_dma; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci dev_info(&pdev->dev, "Ingenic SD/MMC card driver registered\n"); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci dev_info(&pdev->dev, "Using %s, %d-bit mode\n", 114962306a36Sopenharmony_ci host->use_dma ? "DMA" : "PIO", 115062306a36Sopenharmony_ci (mmc->caps & MMC_CAP_8_BIT_DATA) ? 8 : 115162306a36Sopenharmony_ci ((mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1)); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci return 0; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cierr_release_dma: 115662306a36Sopenharmony_ci if (host->use_dma) 115762306a36Sopenharmony_ci jz4740_mmc_release_dma_channels(host); 115862306a36Sopenharmony_cierr_free_irq: 115962306a36Sopenharmony_ci free_irq(host->irq, host); 116062306a36Sopenharmony_cierr_free_host: 116162306a36Sopenharmony_ci mmc_free_host(mmc); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci return ret; 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_cistatic void jz4740_mmc_remove(struct platform_device *pdev) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci struct jz4740_mmc_host *host = platform_get_drvdata(pdev); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci del_timer_sync(&host->timeout_timer); 117162306a36Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, 0xff, false); 117262306a36Sopenharmony_ci jz4740_mmc_reset(host); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci mmc_remove_host(host->mmc); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci free_irq(host->irq, host); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (host->use_dma) 117962306a36Sopenharmony_ci jz4740_mmc_release_dma_channels(host); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci mmc_free_host(host->mmc); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic int jz4740_mmc_suspend(struct device *dev) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci return pinctrl_pm_select_sleep_state(dev); 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic int jz4740_mmc_resume(struct device *dev) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci return pinctrl_select_default_state(dev); 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend, 119562306a36Sopenharmony_ci jz4740_mmc_resume); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic struct platform_driver jz4740_mmc_driver = { 119862306a36Sopenharmony_ci .probe = jz4740_mmc_probe, 119962306a36Sopenharmony_ci .remove_new = jz4740_mmc_remove, 120062306a36Sopenharmony_ci .driver = { 120162306a36Sopenharmony_ci .name = "jz4740-mmc", 120262306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 120362306a36Sopenharmony_ci .of_match_table = of_match_ptr(jz4740_mmc_of_match), 120462306a36Sopenharmony_ci .pm = pm_sleep_ptr(&jz4740_mmc_pm_ops), 120562306a36Sopenharmony_ci }, 120662306a36Sopenharmony_ci}; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cimodule_platform_driver(jz4740_mmc_driver); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ciMODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); 121162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 121262306a36Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 1213