162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Actions Semi Owl SoCs SD/MMC driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 Actions Semi Inc. 662306a36Sopenharmony_ci * Copyright (c) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * TODO: SDIO support 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/dmaengine.h> 1462306a36Sopenharmony_ci#include <linux/dma-direction.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/mmc/host.h> 1862306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 1962306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/reset.h> 2362306a36Sopenharmony_ci#include <linux/spinlock.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * SDC registers 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#define OWL_REG_SD_EN 0x0000 2962306a36Sopenharmony_ci#define OWL_REG_SD_CTL 0x0004 3062306a36Sopenharmony_ci#define OWL_REG_SD_STATE 0x0008 3162306a36Sopenharmony_ci#define OWL_REG_SD_CMD 0x000c 3262306a36Sopenharmony_ci#define OWL_REG_SD_ARG 0x0010 3362306a36Sopenharmony_ci#define OWL_REG_SD_RSPBUF0 0x0014 3462306a36Sopenharmony_ci#define OWL_REG_SD_RSPBUF1 0x0018 3562306a36Sopenharmony_ci#define OWL_REG_SD_RSPBUF2 0x001c 3662306a36Sopenharmony_ci#define OWL_REG_SD_RSPBUF3 0x0020 3762306a36Sopenharmony_ci#define OWL_REG_SD_RSPBUF4 0x0024 3862306a36Sopenharmony_ci#define OWL_REG_SD_DAT 0x0028 3962306a36Sopenharmony_ci#define OWL_REG_SD_BLK_SIZE 0x002c 4062306a36Sopenharmony_ci#define OWL_REG_SD_BLK_NUM 0x0030 4162306a36Sopenharmony_ci#define OWL_REG_SD_BUF_SIZE 0x0034 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* SD_EN Bits */ 4462306a36Sopenharmony_ci#define OWL_SD_EN_RANE BIT(31) 4562306a36Sopenharmony_ci#define OWL_SD_EN_RAN_SEED(x) (((x) & 0x3f) << 24) 4662306a36Sopenharmony_ci#define OWL_SD_EN_S18EN BIT(12) 4762306a36Sopenharmony_ci#define OWL_SD_EN_RESE BIT(10) 4862306a36Sopenharmony_ci#define OWL_SD_EN_DAT1_S BIT(9) 4962306a36Sopenharmony_ci#define OWL_SD_EN_CLK_S BIT(8) 5062306a36Sopenharmony_ci#define OWL_SD_ENABLE BIT(7) 5162306a36Sopenharmony_ci#define OWL_SD_EN_BSEL BIT(6) 5262306a36Sopenharmony_ci#define OWL_SD_EN_SDIOEN BIT(3) 5362306a36Sopenharmony_ci#define OWL_SD_EN_DDREN BIT(2) 5462306a36Sopenharmony_ci#define OWL_SD_EN_DATAWID(x) (((x) & 0x3) << 0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* SD_CTL Bits */ 5762306a36Sopenharmony_ci#define OWL_SD_CTL_TOUTEN BIT(31) 5862306a36Sopenharmony_ci#define OWL_SD_CTL_TOUTCNT(x) (((x) & 0x7f) << 24) 5962306a36Sopenharmony_ci#define OWL_SD_CTL_DELAY_MSK GENMASK(23, 16) 6062306a36Sopenharmony_ci#define OWL_SD_CTL_RDELAY(x) (((x) & 0xf) << 20) 6162306a36Sopenharmony_ci#define OWL_SD_CTL_WDELAY(x) (((x) & 0xf) << 16) 6262306a36Sopenharmony_ci#define OWL_SD_CTL_CMDLEN BIT(13) 6362306a36Sopenharmony_ci#define OWL_SD_CTL_SCC BIT(12) 6462306a36Sopenharmony_ci#define OWL_SD_CTL_TCN(x) (((x) & 0xf) << 8) 6562306a36Sopenharmony_ci#define OWL_SD_CTL_TS BIT(7) 6662306a36Sopenharmony_ci#define OWL_SD_CTL_LBE BIT(6) 6762306a36Sopenharmony_ci#define OWL_SD_CTL_C7EN BIT(5) 6862306a36Sopenharmony_ci#define OWL_SD_CTL_TM(x) (((x) & 0xf) << 0) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define OWL_SD_DELAY_LOW_CLK 0x0f 7162306a36Sopenharmony_ci#define OWL_SD_DELAY_MID_CLK 0x0a 7262306a36Sopenharmony_ci#define OWL_SD_DELAY_HIGH_CLK 0x09 7362306a36Sopenharmony_ci#define OWL_SD_RDELAY_DDR50 0x0a 7462306a36Sopenharmony_ci#define OWL_SD_WDELAY_DDR50 0x08 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* SD_STATE Bits */ 7762306a36Sopenharmony_ci#define OWL_SD_STATE_DAT1BS BIT(18) 7862306a36Sopenharmony_ci#define OWL_SD_STATE_SDIOB_P BIT(17) 7962306a36Sopenharmony_ci#define OWL_SD_STATE_SDIOB_EN BIT(16) 8062306a36Sopenharmony_ci#define OWL_SD_STATE_TOUTE BIT(15) 8162306a36Sopenharmony_ci#define OWL_SD_STATE_BAEP BIT(14) 8262306a36Sopenharmony_ci#define OWL_SD_STATE_MEMRDY BIT(12) 8362306a36Sopenharmony_ci#define OWL_SD_STATE_CMDS BIT(11) 8462306a36Sopenharmony_ci#define OWL_SD_STATE_DAT1AS BIT(10) 8562306a36Sopenharmony_ci#define OWL_SD_STATE_SDIOA_P BIT(9) 8662306a36Sopenharmony_ci#define OWL_SD_STATE_SDIOA_EN BIT(8) 8762306a36Sopenharmony_ci#define OWL_SD_STATE_DAT0S BIT(7) 8862306a36Sopenharmony_ci#define OWL_SD_STATE_TEIE BIT(6) 8962306a36Sopenharmony_ci#define OWL_SD_STATE_TEI BIT(5) 9062306a36Sopenharmony_ci#define OWL_SD_STATE_CLNR BIT(4) 9162306a36Sopenharmony_ci#define OWL_SD_STATE_CLC BIT(3) 9262306a36Sopenharmony_ci#define OWL_SD_STATE_WC16ER BIT(2) 9362306a36Sopenharmony_ci#define OWL_SD_STATE_RC16ER BIT(1) 9462306a36Sopenharmony_ci#define OWL_SD_STATE_CRC7ER BIT(0) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define OWL_CMD_TIMEOUT_MS 30000 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct owl_mmc_host { 9962306a36Sopenharmony_ci struct device *dev; 10062306a36Sopenharmony_ci struct reset_control *reset; 10162306a36Sopenharmony_ci void __iomem *base; 10262306a36Sopenharmony_ci struct clk *clk; 10362306a36Sopenharmony_ci struct completion sdc_complete; 10462306a36Sopenharmony_ci spinlock_t lock; 10562306a36Sopenharmony_ci int irq; 10662306a36Sopenharmony_ci u32 clock; 10762306a36Sopenharmony_ci bool ddr_50; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci enum dma_data_direction dma_dir; 11062306a36Sopenharmony_ci struct dma_chan *dma; 11162306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 11262306a36Sopenharmony_ci struct dma_slave_config dma_cfg; 11362306a36Sopenharmony_ci struct completion dma_complete; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci struct mmc_host *mmc; 11662306a36Sopenharmony_ci struct mmc_request *mrq; 11762306a36Sopenharmony_ci struct mmc_command *cmd; 11862306a36Sopenharmony_ci struct mmc_data *data; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void owl_mmc_update_reg(void __iomem *reg, unsigned int val, bool state) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci unsigned int regval; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci regval = readl(reg); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (state) 12862306a36Sopenharmony_ci regval |= val; 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci regval &= ~val; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci writel(regval, reg); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic irqreturn_t owl_irq_handler(int irq, void *devid) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct owl_mmc_host *owl_host = devid; 13862306a36Sopenharmony_ci u32 state; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci spin_lock(&owl_host->lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci state = readl(owl_host->base + OWL_REG_SD_STATE); 14362306a36Sopenharmony_ci if (state & OWL_SD_STATE_TEI) { 14462306a36Sopenharmony_ci state = readl(owl_host->base + OWL_REG_SD_STATE); 14562306a36Sopenharmony_ci state |= OWL_SD_STATE_TEI; 14662306a36Sopenharmony_ci writel(state, owl_host->base + OWL_REG_SD_STATE); 14762306a36Sopenharmony_ci complete(&owl_host->sdc_complete); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci spin_unlock(&owl_host->lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return IRQ_HANDLED; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void owl_mmc_finish_request(struct owl_mmc_host *owl_host) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct mmc_request *mrq = owl_host->mrq; 15862306a36Sopenharmony_ci struct mmc_data *data = mrq->data; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Should never be NULL */ 16162306a36Sopenharmony_ci WARN_ON(!mrq); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci owl_host->mrq = NULL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (data) 16662306a36Sopenharmony_ci dma_unmap_sg(owl_host->dma->device->dev, data->sg, data->sg_len, 16762306a36Sopenharmony_ci owl_host->dma_dir); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Finally finish request */ 17062306a36Sopenharmony_ci mmc_request_done(owl_host->mmc, mrq); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void owl_mmc_send_cmd(struct owl_mmc_host *owl_host, 17462306a36Sopenharmony_ci struct mmc_command *cmd, 17562306a36Sopenharmony_ci struct mmc_data *data) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci unsigned long timeout; 17862306a36Sopenharmony_ci u32 mode, state, resp[2]; 17962306a36Sopenharmony_ci u32 cmd_rsp_mask = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci init_completion(&owl_host->sdc_complete); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci switch (mmc_resp_type(cmd)) { 18462306a36Sopenharmony_ci case MMC_RSP_NONE: 18562306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(0); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci case MMC_RSP_R1: 18962306a36Sopenharmony_ci if (data) { 19062306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) 19162306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(4); 19262306a36Sopenharmony_ci else 19362306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(5); 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(1); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci case MMC_RSP_R1B: 20262306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(3); 20362306a36Sopenharmony_ci cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci case MMC_RSP_R2: 20762306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(2); 20862306a36Sopenharmony_ci cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci case MMC_RSP_R3: 21262306a36Sopenharmony_ci mode = OWL_SD_CTL_TM(1); 21362306a36Sopenharmony_ci cmd_rsp_mask = OWL_SD_STATE_CLNR; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci default: 21762306a36Sopenharmony_ci dev_warn(owl_host->dev, "Unknown MMC command\n"); 21862306a36Sopenharmony_ci cmd->error = -EINVAL; 21962306a36Sopenharmony_ci return; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Keep current WDELAY and RDELAY */ 22362306a36Sopenharmony_ci mode |= (readl(owl_host->base + OWL_REG_SD_CTL) & (0xff << 16)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Start to send corresponding command type */ 22662306a36Sopenharmony_ci writel(cmd->arg, owl_host->base + OWL_REG_SD_ARG); 22762306a36Sopenharmony_ci writel(cmd->opcode, owl_host->base + OWL_REG_SD_CMD); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Set LBE to send clk at the end of last read block */ 23062306a36Sopenharmony_ci if (data) { 23162306a36Sopenharmony_ci mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0x64000000); 23262306a36Sopenharmony_ci } else { 23362306a36Sopenharmony_ci mode &= ~(OWL_SD_CTL_TOUTEN | OWL_SD_CTL_LBE); 23462306a36Sopenharmony_ci mode |= OWL_SD_CTL_TS; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci owl_host->cmd = cmd; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Start transfer */ 24062306a36Sopenharmony_ci writel(mode, owl_host->base + OWL_REG_SD_CTL); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (data) 24362306a36Sopenharmony_ci return; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci timeout = msecs_to_jiffies(cmd->busy_timeout ? cmd->busy_timeout : 24662306a36Sopenharmony_ci OWL_CMD_TIMEOUT_MS); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!wait_for_completion_timeout(&owl_host->sdc_complete, timeout)) { 24962306a36Sopenharmony_ci dev_err(owl_host->dev, "CMD interrupt timeout\n"); 25062306a36Sopenharmony_ci cmd->error = -ETIMEDOUT; 25162306a36Sopenharmony_ci return; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci state = readl(owl_host->base + OWL_REG_SD_STATE); 25562306a36Sopenharmony_ci if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) { 25662306a36Sopenharmony_ci if (cmd_rsp_mask & state) { 25762306a36Sopenharmony_ci if (state & OWL_SD_STATE_CLNR) { 25862306a36Sopenharmony_ci dev_err(owl_host->dev, "Error CMD_NO_RSP\n"); 25962306a36Sopenharmony_ci cmd->error = -EILSEQ; 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (state & OWL_SD_STATE_CRC7ER) { 26462306a36Sopenharmony_ci dev_err(owl_host->dev, "Error CMD_RSP_CRC\n"); 26562306a36Sopenharmony_ci cmd->error = -EILSEQ; 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (mmc_resp_type(cmd) & MMC_RSP_136) { 27162306a36Sopenharmony_ci cmd->resp[3] = readl(owl_host->base + OWL_REG_SD_RSPBUF0); 27262306a36Sopenharmony_ci cmd->resp[2] = readl(owl_host->base + OWL_REG_SD_RSPBUF1); 27362306a36Sopenharmony_ci cmd->resp[1] = readl(owl_host->base + OWL_REG_SD_RSPBUF2); 27462306a36Sopenharmony_ci cmd->resp[0] = readl(owl_host->base + OWL_REG_SD_RSPBUF3); 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci resp[0] = readl(owl_host->base + OWL_REG_SD_RSPBUF0); 27762306a36Sopenharmony_ci resp[1] = readl(owl_host->base + OWL_REG_SD_RSPBUF1); 27862306a36Sopenharmony_ci cmd->resp[0] = resp[1] << 24 | resp[0] >> 8; 27962306a36Sopenharmony_ci cmd->resp[1] = resp[1] >> 8; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void owl_mmc_dma_complete(void *param) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct owl_mmc_host *owl_host = param; 28762306a36Sopenharmony_ci struct mmc_data *data = owl_host->data; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (data) 29062306a36Sopenharmony_ci complete(&owl_host->dma_complete); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int owl_mmc_prepare_data(struct owl_mmc_host *owl_host, 29462306a36Sopenharmony_ci struct mmc_data *data) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci u32 total; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, OWL_SD_EN_BSEL, 29962306a36Sopenharmony_ci true); 30062306a36Sopenharmony_ci writel(data->blocks, owl_host->base + OWL_REG_SD_BLK_NUM); 30162306a36Sopenharmony_ci writel(data->blksz, owl_host->base + OWL_REG_SD_BLK_SIZE); 30262306a36Sopenharmony_ci total = data->blksz * data->blocks; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (total < 512) 30562306a36Sopenharmony_ci writel(total, owl_host->base + OWL_REG_SD_BUF_SIZE); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci writel(512, owl_host->base + OWL_REG_SD_BUF_SIZE); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) { 31062306a36Sopenharmony_ci owl_host->dma_dir = DMA_TO_DEVICE; 31162306a36Sopenharmony_ci owl_host->dma_cfg.direction = DMA_MEM_TO_DEV; 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci owl_host->dma_dir = DMA_FROM_DEVICE; 31462306a36Sopenharmony_ci owl_host->dma_cfg.direction = DMA_DEV_TO_MEM; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci dma_map_sg(owl_host->dma->device->dev, data->sg, 31862306a36Sopenharmony_ci data->sg_len, owl_host->dma_dir); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dmaengine_slave_config(owl_host->dma, &owl_host->dma_cfg); 32162306a36Sopenharmony_ci owl_host->desc = dmaengine_prep_slave_sg(owl_host->dma, data->sg, 32262306a36Sopenharmony_ci data->sg_len, 32362306a36Sopenharmony_ci owl_host->dma_cfg.direction, 32462306a36Sopenharmony_ci DMA_PREP_INTERRUPT | 32562306a36Sopenharmony_ci DMA_CTRL_ACK); 32662306a36Sopenharmony_ci if (!owl_host->desc) { 32762306a36Sopenharmony_ci dev_err(owl_host->dev, "Can't prepare slave sg\n"); 32862306a36Sopenharmony_ci return -EBUSY; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci owl_host->data = data; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci owl_host->desc->callback = owl_mmc_dma_complete; 33462306a36Sopenharmony_ci owl_host->desc->callback_param = (void *)owl_host; 33562306a36Sopenharmony_ci data->error = 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void owl_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct owl_mmc_host *owl_host = mmc_priv(mmc); 34362306a36Sopenharmony_ci struct mmc_data *data = mrq->data; 34462306a36Sopenharmony_ci int ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci owl_host->mrq = mrq; 34762306a36Sopenharmony_ci if (mrq->data) { 34862306a36Sopenharmony_ci ret = owl_mmc_prepare_data(owl_host, data); 34962306a36Sopenharmony_ci if (ret < 0) { 35062306a36Sopenharmony_ci data->error = ret; 35162306a36Sopenharmony_ci goto err_out; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci init_completion(&owl_host->dma_complete); 35562306a36Sopenharmony_ci dmaengine_submit(owl_host->desc); 35662306a36Sopenharmony_ci dma_async_issue_pending(owl_host->dma); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci owl_mmc_send_cmd(owl_host, mrq->cmd, data); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (data) { 36262306a36Sopenharmony_ci if (!wait_for_completion_timeout(&owl_host->sdc_complete, 36362306a36Sopenharmony_ci 10 * HZ)) { 36462306a36Sopenharmony_ci dev_err(owl_host->dev, "CMD interrupt timeout\n"); 36562306a36Sopenharmony_ci mrq->cmd->error = -ETIMEDOUT; 36662306a36Sopenharmony_ci dmaengine_terminate_all(owl_host->dma); 36762306a36Sopenharmony_ci goto err_out; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!wait_for_completion_timeout(&owl_host->dma_complete, 37162306a36Sopenharmony_ci 5 * HZ)) { 37262306a36Sopenharmony_ci dev_err(owl_host->dev, "DMA interrupt timeout\n"); 37362306a36Sopenharmony_ci mrq->cmd->error = -ETIMEDOUT; 37462306a36Sopenharmony_ci dmaengine_terminate_all(owl_host->dma); 37562306a36Sopenharmony_ci goto err_out; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (data->stop) 37962306a36Sopenharmony_ci owl_mmc_send_cmd(owl_host, data->stop, NULL); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cierr_out: 38562306a36Sopenharmony_ci owl_mmc_finish_request(owl_host); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int owl_mmc_set_clk_rate(struct owl_mmc_host *owl_host, 38962306a36Sopenharmony_ci unsigned int rate) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci unsigned long clk_rate; 39262306a36Sopenharmony_ci int ret; 39362306a36Sopenharmony_ci u32 reg; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci reg = readl(owl_host->base + OWL_REG_SD_CTL); 39662306a36Sopenharmony_ci reg &= ~OWL_SD_CTL_DELAY_MSK; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Set RDELAY and WDELAY based on the clock */ 39962306a36Sopenharmony_ci if (rate <= 1000000) { 40062306a36Sopenharmony_ci writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) | 40162306a36Sopenharmony_ci OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK), 40262306a36Sopenharmony_ci owl_host->base + OWL_REG_SD_CTL); 40362306a36Sopenharmony_ci } else if ((rate > 1000000) && (rate <= 26000000)) { 40462306a36Sopenharmony_ci writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) | 40562306a36Sopenharmony_ci OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK), 40662306a36Sopenharmony_ci owl_host->base + OWL_REG_SD_CTL); 40762306a36Sopenharmony_ci } else if ((rate > 26000000) && (rate <= 52000000) && !owl_host->ddr_50) { 40862306a36Sopenharmony_ci writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_HIGH_CLK) | 40962306a36Sopenharmony_ci OWL_SD_CTL_WDELAY(OWL_SD_DELAY_HIGH_CLK), 41062306a36Sopenharmony_ci owl_host->base + OWL_REG_SD_CTL); 41162306a36Sopenharmony_ci /* DDR50 mode has special delay chain */ 41262306a36Sopenharmony_ci } else if ((rate > 26000000) && (rate <= 52000000) && owl_host->ddr_50) { 41362306a36Sopenharmony_ci writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_DDR50) | 41462306a36Sopenharmony_ci OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_DDR50), 41562306a36Sopenharmony_ci owl_host->base + OWL_REG_SD_CTL); 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci dev_err(owl_host->dev, "SD clock rate not supported\n"); 41862306a36Sopenharmony_ci return -EINVAL; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci clk_rate = clk_round_rate(owl_host->clk, rate << 1); 42262306a36Sopenharmony_ci ret = clk_set_rate(owl_host->clk, clk_rate); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void owl_mmc_set_clk(struct owl_mmc_host *owl_host, struct mmc_ios *ios) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (!ios->clock) 43062306a36Sopenharmony_ci return; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci owl_host->clock = ios->clock; 43362306a36Sopenharmony_ci owl_mmc_set_clk_rate(owl_host, ios->clock); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void owl_mmc_set_bus_width(struct owl_mmc_host *owl_host, 43762306a36Sopenharmony_ci struct mmc_ios *ios) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci u32 reg; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci reg = readl(owl_host->base + OWL_REG_SD_EN); 44262306a36Sopenharmony_ci reg &= ~0x03; 44362306a36Sopenharmony_ci switch (ios->bus_width) { 44462306a36Sopenharmony_ci case MMC_BUS_WIDTH_1: 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 44762306a36Sopenharmony_ci reg |= OWL_SD_EN_DATAWID(1); 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case MMC_BUS_WIDTH_8: 45062306a36Sopenharmony_ci reg |= OWL_SD_EN_DATAWID(2); 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci writel(reg, owl_host->base + OWL_REG_SD_EN); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void owl_mmc_ctr_reset(struct owl_mmc_host *owl_host) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci reset_control_assert(owl_host->reset); 46062306a36Sopenharmony_ci udelay(20); 46162306a36Sopenharmony_ci reset_control_deassert(owl_host->reset); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic void owl_mmc_power_on(struct owl_mmc_host *owl_host) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci u32 mode; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci init_completion(&owl_host->sdc_complete); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Enable transfer end IRQ */ 47162306a36Sopenharmony_ci owl_mmc_update_reg(owl_host->base + OWL_REG_SD_STATE, 47262306a36Sopenharmony_ci OWL_SD_STATE_TEIE, true); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Send init clk */ 47562306a36Sopenharmony_ci mode = (readl(owl_host->base + OWL_REG_SD_CTL) & (0xff << 16)); 47662306a36Sopenharmony_ci mode |= OWL_SD_CTL_TS | OWL_SD_CTL_TCN(5) | OWL_SD_CTL_TM(8); 47762306a36Sopenharmony_ci writel(mode, owl_host->base + OWL_REG_SD_CTL); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (!wait_for_completion_timeout(&owl_host->sdc_complete, HZ)) { 48062306a36Sopenharmony_ci dev_err(owl_host->dev, "CMD interrupt timeout\n"); 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void owl_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct owl_mmc_host *owl_host = mmc_priv(mmc); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci switch (ios->power_mode) { 49062306a36Sopenharmony_ci case MMC_POWER_UP: 49162306a36Sopenharmony_ci dev_dbg(owl_host->dev, "Powering card up\n"); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Reset the SDC controller to clear all previous states */ 49462306a36Sopenharmony_ci owl_mmc_ctr_reset(owl_host); 49562306a36Sopenharmony_ci clk_prepare_enable(owl_host->clk); 49662306a36Sopenharmony_ci writel(OWL_SD_ENABLE | OWL_SD_EN_RESE, 49762306a36Sopenharmony_ci owl_host->base + OWL_REG_SD_EN); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci case MMC_POWER_ON: 50262306a36Sopenharmony_ci dev_dbg(owl_host->dev, "Powering card on\n"); 50362306a36Sopenharmony_ci owl_mmc_power_on(owl_host); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci case MMC_POWER_OFF: 50862306a36Sopenharmony_ci dev_dbg(owl_host->dev, "Powering card off\n"); 50962306a36Sopenharmony_ci clk_disable_unprepare(owl_host->clk); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci default: 51462306a36Sopenharmony_ci dev_dbg(owl_host->dev, "Ignoring unknown card power state\n"); 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (ios->clock != owl_host->clock) 51962306a36Sopenharmony_ci owl_mmc_set_clk(owl_host, ios); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci owl_mmc_set_bus_width(owl_host, ios); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Enable DDR mode if requested */ 52462306a36Sopenharmony_ci if (ios->timing == MMC_TIMING_UHS_DDR50) { 52562306a36Sopenharmony_ci owl_host->ddr_50 = true; 52662306a36Sopenharmony_ci owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, 52762306a36Sopenharmony_ci OWL_SD_EN_DDREN, true); 52862306a36Sopenharmony_ci } else { 52962306a36Sopenharmony_ci owl_host->ddr_50 = false; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int owl_mmc_start_signal_voltage_switch(struct mmc_host *mmc, 53462306a36Sopenharmony_ci struct mmc_ios *ios) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct owl_mmc_host *owl_host = mmc_priv(mmc); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* It is enough to change the pad ctrl bit for voltage switch */ 53962306a36Sopenharmony_ci switch (ios->signal_voltage) { 54062306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_330: 54162306a36Sopenharmony_ci owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, 54262306a36Sopenharmony_ci OWL_SD_EN_S18EN, false); 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_180: 54562306a36Sopenharmony_ci owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, 54662306a36Sopenharmony_ci OWL_SD_EN_S18EN, true); 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci default: 54962306a36Sopenharmony_ci return -ENOTSUPP; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic const struct mmc_host_ops owl_mmc_ops = { 55662306a36Sopenharmony_ci .request = owl_mmc_request, 55762306a36Sopenharmony_ci .set_ios = owl_mmc_set_ios, 55862306a36Sopenharmony_ci .get_ro = mmc_gpio_get_ro, 55962306a36Sopenharmony_ci .get_cd = mmc_gpio_get_cd, 56062306a36Sopenharmony_ci .start_signal_voltage_switch = owl_mmc_start_signal_voltage_switch, 56162306a36Sopenharmony_ci}; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int owl_mmc_probe(struct platform_device *pdev) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct owl_mmc_host *owl_host; 56662306a36Sopenharmony_ci struct mmc_host *mmc; 56762306a36Sopenharmony_ci struct resource *res; 56862306a36Sopenharmony_ci int ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct owl_mmc_host), &pdev->dev); 57162306a36Sopenharmony_ci if (!mmc) { 57262306a36Sopenharmony_ci dev_err(&pdev->dev, "mmc alloc host failed\n"); 57362306a36Sopenharmony_ci return -ENOMEM; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci platform_set_drvdata(pdev, mmc); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci owl_host = mmc_priv(mmc); 57862306a36Sopenharmony_ci owl_host->dev = &pdev->dev; 57962306a36Sopenharmony_ci owl_host->mmc = mmc; 58062306a36Sopenharmony_ci spin_lock_init(&owl_host->lock); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci owl_host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 58362306a36Sopenharmony_ci if (IS_ERR(owl_host->base)) { 58462306a36Sopenharmony_ci ret = PTR_ERR(owl_host->base); 58562306a36Sopenharmony_ci goto err_free_host; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci owl_host->clk = devm_clk_get(&pdev->dev, NULL); 58962306a36Sopenharmony_ci if (IS_ERR(owl_host->clk)) { 59062306a36Sopenharmony_ci dev_err(&pdev->dev, "No clock defined\n"); 59162306a36Sopenharmony_ci ret = PTR_ERR(owl_host->clk); 59262306a36Sopenharmony_ci goto err_free_host; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci owl_host->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); 59662306a36Sopenharmony_ci if (IS_ERR(owl_host->reset)) { 59762306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not get reset control\n"); 59862306a36Sopenharmony_ci ret = PTR_ERR(owl_host->reset); 59962306a36Sopenharmony_ci goto err_free_host; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci mmc->ops = &owl_mmc_ops; 60362306a36Sopenharmony_ci mmc->max_blk_count = 512; 60462306a36Sopenharmony_ci mmc->max_blk_size = 512; 60562306a36Sopenharmony_ci mmc->max_segs = 256; 60662306a36Sopenharmony_ci mmc->max_seg_size = 262144; 60762306a36Sopenharmony_ci mmc->max_req_size = 262144; 60862306a36Sopenharmony_ci /* 100kHz ~ 52MHz */ 60962306a36Sopenharmony_ci mmc->f_min = 100000; 61062306a36Sopenharmony_ci mmc->f_max = 52000000; 61162306a36Sopenharmony_ci mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | 61262306a36Sopenharmony_ci MMC_CAP_4_BIT_DATA; 61362306a36Sopenharmony_ci mmc->caps2 = (MMC_CAP2_BOOTPART_NOACC | MMC_CAP2_NO_SDIO); 61462306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | 61562306a36Sopenharmony_ci MMC_VDD_165_195; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ret = mmc_of_parse(mmc); 61862306a36Sopenharmony_ci if (ret) 61962306a36Sopenharmony_ci goto err_free_host; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 62262306a36Sopenharmony_ci pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 62362306a36Sopenharmony_ci owl_host->dma = dma_request_chan(&pdev->dev, "mmc"); 62462306a36Sopenharmony_ci if (IS_ERR(owl_host->dma)) { 62562306a36Sopenharmony_ci dev_err(owl_host->dev, "Failed to get external DMA channel.\n"); 62662306a36Sopenharmony_ci ret = PTR_ERR(owl_host->dma); 62762306a36Sopenharmony_ci goto err_free_host; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci dev_info(&pdev->dev, "Using %s for DMA transfers\n", 63162306a36Sopenharmony_ci dma_chan_name(owl_host->dma)); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci owl_host->dma_cfg.src_addr = res->start + OWL_REG_SD_DAT; 63462306a36Sopenharmony_ci owl_host->dma_cfg.dst_addr = res->start + OWL_REG_SD_DAT; 63562306a36Sopenharmony_ci owl_host->dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 63662306a36Sopenharmony_ci owl_host->dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 63762306a36Sopenharmony_ci owl_host->dma_cfg.device_fc = false; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci owl_host->irq = platform_get_irq(pdev, 0); 64062306a36Sopenharmony_ci if (owl_host->irq < 0) { 64162306a36Sopenharmony_ci ret = owl_host->irq; 64262306a36Sopenharmony_ci goto err_release_channel; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, owl_host->irq, owl_irq_handler, 64662306a36Sopenharmony_ci 0, dev_name(&pdev->dev), owl_host); 64762306a36Sopenharmony_ci if (ret) { 64862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq %d\n", 64962306a36Sopenharmony_ci owl_host->irq); 65062306a36Sopenharmony_ci goto err_release_channel; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ret = mmc_add_host(mmc); 65462306a36Sopenharmony_ci if (ret) { 65562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to add host\n"); 65662306a36Sopenharmony_ci goto err_release_channel; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Owl MMC Controller Initialized\n"); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cierr_release_channel: 66462306a36Sopenharmony_ci dma_release_channel(owl_host->dma); 66562306a36Sopenharmony_cierr_free_host: 66662306a36Sopenharmony_ci mmc_free_host(mmc); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void owl_mmc_remove(struct platform_device *pdev) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct mmc_host *mmc = platform_get_drvdata(pdev); 67462306a36Sopenharmony_ci struct owl_mmc_host *owl_host = mmc_priv(mmc); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci mmc_remove_host(mmc); 67762306a36Sopenharmony_ci disable_irq(owl_host->irq); 67862306a36Sopenharmony_ci dma_release_channel(owl_host->dma); 67962306a36Sopenharmony_ci mmc_free_host(mmc); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic const struct of_device_id owl_mmc_of_match[] = { 68362306a36Sopenharmony_ci {.compatible = "actions,owl-mmc",}, 68462306a36Sopenharmony_ci { /* sentinel */ } 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, owl_mmc_of_match); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic struct platform_driver owl_mmc_driver = { 68962306a36Sopenharmony_ci .driver = { 69062306a36Sopenharmony_ci .name = "owl_mmc", 69162306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 69262306a36Sopenharmony_ci .of_match_table = owl_mmc_of_match, 69362306a36Sopenharmony_ci }, 69462306a36Sopenharmony_ci .probe = owl_mmc_probe, 69562306a36Sopenharmony_ci .remove_new = owl_mmc_remove, 69662306a36Sopenharmony_ci}; 69762306a36Sopenharmony_cimodule_platform_driver(owl_mmc_driver); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ciMODULE_DESCRIPTION("Actions Semi Owl SoCs SD/MMC Driver"); 70062306a36Sopenharmony_ciMODULE_AUTHOR("Actions Semi"); 70162306a36Sopenharmony_ciMODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 70262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 703