162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2017-2018 Socionext Inc. 462306a36Sopenharmony_ci// Author: Masahiro Yamada <yamada.masahiro@socionext.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/bitops.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/mfd/tmio.h> 1362306a36Sopenharmony_ci#include <linux/mmc/host.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/reset.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "tmio_mmc.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define UNIPHIER_SD_CLK_CTL_DIV1024 BIT(16) 2462306a36Sopenharmony_ci#define UNIPHIER_SD_CLK_CTL_DIV1 BIT(10) 2562306a36Sopenharmony_ci#define UNIPHIER_SD_CLKCTL_OFFEN BIT(9) // auto SDCLK stop 2662306a36Sopenharmony_ci#define UNIPHIER_SD_CC_EXT_MODE 0x1b0 2762306a36Sopenharmony_ci#define UNIPHIER_SD_CC_EXT_MODE_DMA BIT(1) 2862306a36Sopenharmony_ci#define UNIPHIER_SD_HOST_MODE 0x1c8 2962306a36Sopenharmony_ci#define UNIPHIER_SD_VOLT 0x1e4 3062306a36Sopenharmony_ci#define UNIPHIER_SD_VOLT_MASK GENMASK(1, 0) 3162306a36Sopenharmony_ci#define UNIPHIER_SD_VOLT_OFF 0 3262306a36Sopenharmony_ci#define UNIPHIER_SD_VOLT_330 1 // 3.3V signal 3362306a36Sopenharmony_ci#define UNIPHIER_SD_VOLT_180 2 // 1.8V signal 3462306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE 0x410 3562306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_DIR_MASK GENMASK(17, 16) 3662306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_DIR_TO_DEV 0 3762306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_DIR_FROM_DEV 1 3862306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_WIDTH_MASK GENMASK(5, 4) 3962306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_WIDTH_8 0 4062306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_WIDTH_16 1 4162306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_WIDTH_32 2 4262306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_WIDTH_64 3 4362306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_MODE_ADDR_INC BIT(0) // 1: inc, 0: fixed 4462306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_CTL 0x414 4562306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_CTL_START BIT(0) // start DMA (auto cleared) 4662306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_RST 0x418 4762306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_RST_CH1 BIT(9) 4862306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_RST_CH0 BIT(8) 4962306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_ADDR_L 0x440 5062306a36Sopenharmony_ci#define UNIPHIER_SD_DMA_ADDR_H 0x444 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* SD control */ 5362306a36Sopenharmony_ci#define UNIPHIER_SDCTRL_CHOFFSET 0x200 5462306a36Sopenharmony_ci#define UNIPHIER_SDCTRL_MODE 0x30 5562306a36Sopenharmony_ci#define UNIPHIER_SDCTRL_MODE_UHS1MOD BIT(15) 5662306a36Sopenharmony_ci#define UNIPHIER_SDCTRL_MODE_SDRSEL BIT(14) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * IP is extended to support various features: built-in DMA engine, 6062306a36Sopenharmony_ci * 1/1024 divisor, etc. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci#define UNIPHIER_SD_CAP_EXTENDED_IP BIT(0) 6362306a36Sopenharmony_ci/* RX channel of the built-in DMA controller is broken (Pro5) */ 6462306a36Sopenharmony_ci#define UNIPHIER_SD_CAP_BROKEN_DMA_RX BIT(1) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct uniphier_sd_priv { 6762306a36Sopenharmony_ci struct tmio_mmc_data tmio_data; 6862306a36Sopenharmony_ci struct pinctrl *pinctrl; 6962306a36Sopenharmony_ci struct pinctrl_state *pinstate_uhs; 7062306a36Sopenharmony_ci struct clk *clk; 7162306a36Sopenharmony_ci struct reset_control *rst; 7262306a36Sopenharmony_ci struct reset_control *rst_br; 7362306a36Sopenharmony_ci struct reset_control *rst_hw; 7462306a36Sopenharmony_ci struct dma_chan *chan; 7562306a36Sopenharmony_ci enum dma_data_direction dma_dir; 7662306a36Sopenharmony_ci struct regmap *sdctrl_regmap; 7762306a36Sopenharmony_ci u32 sdctrl_ch; 7862306a36Sopenharmony_ci unsigned long clk_rate; 7962306a36Sopenharmony_ci unsigned long caps; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void *uniphier_sd_priv(struct tmio_mmc_host *host) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return container_of(host->pdata, struct uniphier_sd_priv, tmio_data); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? DMA_ENABLE_DMASDRW : 0); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* external DMA engine */ 9362306a36Sopenharmony_cistatic void uniphier_sd_external_dma_issue(struct tasklet_struct *t) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue); 9662306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 1); 9962306a36Sopenharmony_ci dma_async_issue_pending(priv->chan); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void uniphier_sd_external_dma_callback(void *param, 10362306a36Sopenharmony_ci const struct dmaengine_result *result) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct tmio_mmc_host *host = param; 10662306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 10762306a36Sopenharmony_ci unsigned long flags; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci dma_unmap_sg(mmc_dev(host->mmc), host->sg_ptr, host->sg_len, 11062306a36Sopenharmony_ci priv->dma_dir); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (result->result == DMA_TRANS_NOERROR) { 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * When the external DMA engine is enabled, strangely enough, 11762306a36Sopenharmony_ci * the DATAEND flag can be asserted even if the DMA engine has 11862306a36Sopenharmony_ci * not been kicked yet. Enable the TMIO_STAT_DATAEND irq only 11962306a36Sopenharmony_ci * after we make sure the DMA engine finishes the transfer, 12062306a36Sopenharmony_ci * hence, in this callback. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci host->data->error = -ETIMEDOUT; 12562306a36Sopenharmony_ci tmio_mmc_do_data_irq(host); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void uniphier_sd_external_dma_start(struct tmio_mmc_host *host, 13262306a36Sopenharmony_ci struct mmc_data *data) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 13562306a36Sopenharmony_ci enum dma_transfer_direction dma_tx_dir; 13662306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 13762306a36Sopenharmony_ci dma_cookie_t cookie; 13862306a36Sopenharmony_ci int sg_len; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!priv->chan) 14162306a36Sopenharmony_ci goto force_pio; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) { 14462306a36Sopenharmony_ci priv->dma_dir = DMA_FROM_DEVICE; 14562306a36Sopenharmony_ci dma_tx_dir = DMA_DEV_TO_MEM; 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci priv->dma_dir = DMA_TO_DEVICE; 14862306a36Sopenharmony_ci dma_tx_dir = DMA_MEM_TO_DEV; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci sg_len = dma_map_sg(mmc_dev(host->mmc), host->sg_ptr, host->sg_len, 15262306a36Sopenharmony_ci priv->dma_dir); 15362306a36Sopenharmony_ci if (sg_len == 0) 15462306a36Sopenharmony_ci goto force_pio; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(priv->chan, host->sg_ptr, sg_len, 15762306a36Sopenharmony_ci dma_tx_dir, DMA_CTRL_ACK); 15862306a36Sopenharmony_ci if (!desc) 15962306a36Sopenharmony_ci goto unmap_sg; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci desc->callback_result = uniphier_sd_external_dma_callback; 16262306a36Sopenharmony_ci desc->callback_param = host; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci cookie = dmaengine_submit(desc); 16562306a36Sopenharmony_ci if (cookie < 0) 16662306a36Sopenharmony_ci goto unmap_sg; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci host->dma_on = true; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciunmap_sg: 17362306a36Sopenharmony_ci dma_unmap_sg(mmc_dev(host->mmc), host->sg_ptr, host->sg_len, 17462306a36Sopenharmony_ci priv->dma_dir); 17562306a36Sopenharmony_ciforce_pio: 17662306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 0); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void uniphier_sd_external_dma_enable(struct tmio_mmc_host *host, 18062306a36Sopenharmony_ci bool enable) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void uniphier_sd_external_dma_request(struct tmio_mmc_host *host, 18562306a36Sopenharmony_ci struct tmio_mmc_data *pdata) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 18862306a36Sopenharmony_ci struct dma_chan *chan; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci chan = dma_request_chan(mmc_dev(host->mmc), "rx-tx"); 19162306a36Sopenharmony_ci if (IS_ERR(chan)) { 19262306a36Sopenharmony_ci dev_warn(mmc_dev(host->mmc), 19362306a36Sopenharmony_ci "failed to request DMA channel. falling back to PIO\n"); 19462306a36Sopenharmony_ci return; /* just use PIO even for -EPROBE_DEFER */ 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* this driver uses a single channel for both RX an TX */ 19862306a36Sopenharmony_ci priv->chan = chan; 19962306a36Sopenharmony_ci host->chan_rx = chan; 20062306a36Sopenharmony_ci host->chan_tx = chan; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void uniphier_sd_external_dma_release(struct tmio_mmc_host *host) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (priv->chan) 21062306a36Sopenharmony_ci dma_release_channel(priv->chan); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void uniphier_sd_external_dma_abort(struct tmio_mmc_host *host) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 0); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (priv->chan) 22062306a36Sopenharmony_ci dmaengine_terminate_sync(priv->chan); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void uniphier_sd_external_dma_dataend(struct tmio_mmc_host *host) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 0); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci tmio_mmc_do_data_irq(host); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = { 23162306a36Sopenharmony_ci .start = uniphier_sd_external_dma_start, 23262306a36Sopenharmony_ci .enable = uniphier_sd_external_dma_enable, 23362306a36Sopenharmony_ci .request = uniphier_sd_external_dma_request, 23462306a36Sopenharmony_ci .release = uniphier_sd_external_dma_release, 23562306a36Sopenharmony_ci .abort = uniphier_sd_external_dma_abort, 23662306a36Sopenharmony_ci .dataend = uniphier_sd_external_dma_dataend, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_issue(struct tasklet_struct *t) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue); 24262306a36Sopenharmony_ci unsigned long flags; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 24562306a36Sopenharmony_ci tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); 24662306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 1); 24962306a36Sopenharmony_ci writel(UNIPHIER_SD_DMA_CTL_START, host->ctl + UNIPHIER_SD_DMA_CTL); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_start(struct tmio_mmc_host *host, 25362306a36Sopenharmony_ci struct mmc_data *data) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 25662306a36Sopenharmony_ci struct scatterlist *sg = host->sg_ptr; 25762306a36Sopenharmony_ci dma_addr_t dma_addr; 25862306a36Sopenharmony_ci unsigned int dma_mode_dir; 25962306a36Sopenharmony_ci u32 dma_mode; 26062306a36Sopenharmony_ci int sg_len; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if ((data->flags & MMC_DATA_READ) && !host->chan_rx) 26362306a36Sopenharmony_ci goto force_pio; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (WARN_ON(host->sg_len != 1)) 26662306a36Sopenharmony_ci goto force_pio; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!IS_ALIGNED(sg->offset, 8)) 26962306a36Sopenharmony_ci goto force_pio; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) { 27262306a36Sopenharmony_ci priv->dma_dir = DMA_FROM_DEVICE; 27362306a36Sopenharmony_ci dma_mode_dir = UNIPHIER_SD_DMA_MODE_DIR_FROM_DEV; 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci priv->dma_dir = DMA_TO_DEVICE; 27662306a36Sopenharmony_ci dma_mode_dir = UNIPHIER_SD_DMA_MODE_DIR_TO_DEV; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci sg_len = dma_map_sg(mmc_dev(host->mmc), sg, 1, priv->dma_dir); 28062306a36Sopenharmony_ci if (sg_len == 0) 28162306a36Sopenharmony_ci goto force_pio; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dma_mode = FIELD_PREP(UNIPHIER_SD_DMA_MODE_DIR_MASK, dma_mode_dir); 28462306a36Sopenharmony_ci dma_mode |= FIELD_PREP(UNIPHIER_SD_DMA_MODE_WIDTH_MASK, 28562306a36Sopenharmony_ci UNIPHIER_SD_DMA_MODE_WIDTH_64); 28662306a36Sopenharmony_ci dma_mode |= UNIPHIER_SD_DMA_MODE_ADDR_INC; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci writel(dma_mode, host->ctl + UNIPHIER_SD_DMA_MODE); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci dma_addr = sg_dma_address(data->sg); 29162306a36Sopenharmony_ci writel(lower_32_bits(dma_addr), host->ctl + UNIPHIER_SD_DMA_ADDR_L); 29262306a36Sopenharmony_ci writel(upper_32_bits(dma_addr), host->ctl + UNIPHIER_SD_DMA_ADDR_H); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci host->dma_on = true; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ciforce_pio: 29862306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 0); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_enable(struct tmio_mmc_host *host, 30262306a36Sopenharmony_ci bool enable) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host, 30762306a36Sopenharmony_ci struct tmio_mmc_data *pdata) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * Due to a hardware bug, Pro5 cannot use DMA for RX. 31362306a36Sopenharmony_ci * We can still use DMA for TX, but PIO for RX. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (!(priv->caps & UNIPHIER_SD_CAP_BROKEN_DMA_RX)) 31662306a36Sopenharmony_ci host->chan_rx = (void *)0xdeadbeaf; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci host->chan_tx = (void *)0xdeadbeaf; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci /* Each value is set to zero to assume "disabling" each DMA */ 32662306a36Sopenharmony_ci host->chan_rx = NULL; 32762306a36Sopenharmony_ci host->chan_tx = NULL; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_abort(struct tmio_mmc_host *host) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci u32 tmp; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 0); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci tmp = readl(host->ctl + UNIPHIER_SD_DMA_RST); 33762306a36Sopenharmony_ci tmp &= ~(UNIPHIER_SD_DMA_RST_CH1 | UNIPHIER_SD_DMA_RST_CH0); 33862306a36Sopenharmony_ci writel(tmp, host->ctl + UNIPHIER_SD_DMA_RST); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci tmp |= UNIPHIER_SD_DMA_RST_CH1 | UNIPHIER_SD_DMA_RST_CH0; 34162306a36Sopenharmony_ci writel(tmp, host->ctl + UNIPHIER_SD_DMA_RST); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic void uniphier_sd_internal_dma_dataend(struct tmio_mmc_host *host) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci uniphier_sd_dma_endisable(host, 0); 34962306a36Sopenharmony_ci dma_unmap_sg(mmc_dev(host->mmc), host->sg_ptr, 1, priv->dma_dir); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci tmio_mmc_do_data_irq(host); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const struct tmio_mmc_dma_ops uniphier_sd_internal_dma_ops = { 35562306a36Sopenharmony_ci .start = uniphier_sd_internal_dma_start, 35662306a36Sopenharmony_ci .enable = uniphier_sd_internal_dma_enable, 35762306a36Sopenharmony_ci .request = uniphier_sd_internal_dma_request, 35862306a36Sopenharmony_ci .release = uniphier_sd_internal_dma_release, 35962306a36Sopenharmony_ci .abort = uniphier_sd_internal_dma_abort, 36062306a36Sopenharmony_ci .dataend = uniphier_sd_internal_dma_dataend, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int uniphier_sd_clk_enable(struct tmio_mmc_host *host) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 36662306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 37062306a36Sopenharmony_ci if (ret) 37162306a36Sopenharmony_ci return ret; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ret = clk_set_rate(priv->clk, ULONG_MAX); 37462306a36Sopenharmony_ci if (ret) 37562306a36Sopenharmony_ci goto disable_clk; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci priv->clk_rate = clk_get_rate(priv->clk); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* If max-frequency property is set, use it. */ 38062306a36Sopenharmony_ci if (!mmc->f_max) 38162306a36Sopenharmony_ci mmc->f_max = priv->clk_rate; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * 1/512 is the finest divisor in the original IP. Newer versions 38562306a36Sopenharmony_ci * also supports 1/1024 divisor. (UniPhier-specific extension) 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP) 38862306a36Sopenharmony_ci mmc->f_min = priv->clk_rate / 1024; 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci mmc->f_min = priv->clk_rate / 512; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = reset_control_deassert(priv->rst); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci goto disable_clk; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = reset_control_deassert(priv->rst_br); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci goto assert_rst; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciassert_rst: 40362306a36Sopenharmony_ci reset_control_assert(priv->rst); 40462306a36Sopenharmony_cidisable_clk: 40562306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void uniphier_sd_clk_disable(struct tmio_mmc_host *host) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci reset_control_assert(priv->rst_br); 41562306a36Sopenharmony_ci reset_control_assert(priv->rst); 41662306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void uniphier_sd_hw_reset(struct mmc_host *mmc) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct tmio_mmc_host *host = mmc_priv(mmc); 42262306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci reset_control_assert(priv->rst_hw); 42562306a36Sopenharmony_ci /* For eMMC, minimum is 1us but give it 9us for good measure */ 42662306a36Sopenharmony_ci udelay(9); 42762306a36Sopenharmony_ci reset_control_deassert(priv->rst_hw); 42862306a36Sopenharmony_ci /* For eMMC, minimum is 200us but give it 300us for good measure */ 42962306a36Sopenharmony_ci usleep_range(300, 1000); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void uniphier_sd_speed_switch(struct tmio_mmc_host *host) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 43562306a36Sopenharmony_ci unsigned int offset; 43662306a36Sopenharmony_ci u32 val = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!(host->mmc->caps & MMC_CAP_UHS)) 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR50 || 44262306a36Sopenharmony_ci host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) 44362306a36Sopenharmony_ci val = UNIPHIER_SDCTRL_MODE_SDRSEL; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch 44662306a36Sopenharmony_ci + UNIPHIER_SDCTRL_MODE; 44762306a36Sopenharmony_ci regmap_write_bits(priv->sdctrl_regmap, offset, 44862306a36Sopenharmony_ci UNIPHIER_SDCTRL_MODE_SDRSEL, val); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void uniphier_sd_uhs_enable(struct tmio_mmc_host *host, bool uhs_en) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 45462306a36Sopenharmony_ci unsigned int offset; 45562306a36Sopenharmony_ci u32 val; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!(host->mmc->caps & MMC_CAP_UHS)) 45862306a36Sopenharmony_ci return; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci val = (uhs_en) ? UNIPHIER_SDCTRL_MODE_UHS1MOD : 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch 46362306a36Sopenharmony_ci + UNIPHIER_SDCTRL_MODE; 46462306a36Sopenharmony_ci regmap_write_bits(priv->sdctrl_regmap, offset, 46562306a36Sopenharmony_ci UNIPHIER_SDCTRL_MODE_UHS1MOD, val); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic void uniphier_sd_set_clock(struct tmio_mmc_host *host, 46962306a36Sopenharmony_ci unsigned int clock) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 47262306a36Sopenharmony_ci unsigned long divisor; 47362306a36Sopenharmony_ci u32 tmp; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci tmp = readl(host->ctl + (CTL_SD_CARD_CLK_CTL << 1)); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* stop the clock before changing its rate to avoid a glitch signal */ 47862306a36Sopenharmony_ci tmp &= ~CLK_CTL_SCLKEN; 47962306a36Sopenharmony_ci writel(tmp, host->ctl + (CTL_SD_CARD_CLK_CTL << 1)); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci uniphier_sd_speed_switch(host); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (clock == 0) 48462306a36Sopenharmony_ci return; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci tmp &= ~UNIPHIER_SD_CLK_CTL_DIV1024; 48762306a36Sopenharmony_ci tmp &= ~UNIPHIER_SD_CLK_CTL_DIV1; 48862306a36Sopenharmony_ci tmp &= ~CLK_CTL_DIV_MASK; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci divisor = priv->clk_rate / clock; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* 49362306a36Sopenharmony_ci * In the original IP, bit[7:0] represents the divisor. 49462306a36Sopenharmony_ci * bit7 set: 1/512, ... bit0 set:1/4, all bits clear: 1/2 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * The IP does not define a way to achieve 1/1. For UniPhier variants, 49762306a36Sopenharmony_ci * bit10 is used for 1/1. Newer versions of UniPhier variants use 49862306a36Sopenharmony_ci * bit16 for 1/1024. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci if (divisor <= 1) 50162306a36Sopenharmony_ci tmp |= UNIPHIER_SD_CLK_CTL_DIV1; 50262306a36Sopenharmony_ci else if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP && divisor > 512) 50362306a36Sopenharmony_ci tmp |= UNIPHIER_SD_CLK_CTL_DIV1024; 50462306a36Sopenharmony_ci else 50562306a36Sopenharmony_ci tmp |= roundup_pow_of_two(divisor) >> 2; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci writel(tmp, host->ctl + (CTL_SD_CARD_CLK_CTL << 1)); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci tmp |= CLK_CTL_SCLKEN; 51062306a36Sopenharmony_ci writel(tmp, host->ctl + (CTL_SD_CARD_CLK_CTL << 1)); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void uniphier_sd_host_init(struct tmio_mmc_host *host) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 51662306a36Sopenharmony_ci u32 val; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * Connected to 32bit AXI. 52062306a36Sopenharmony_ci * This register holds settings for SoC-specific internal bus 52162306a36Sopenharmony_ci * connection. What is worse, the register spec was changed, 52262306a36Sopenharmony_ci * breaking the backward compatibility. Write an appropriate 52362306a36Sopenharmony_ci * value depending on a flag associated with a compatible string. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP) 52662306a36Sopenharmony_ci val = 0x00000101; 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci val = 0x00000000; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci writel(val, host->ctl + UNIPHIER_SD_HOST_MODE); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci val = 0; 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * If supported, the controller can automatically 53562306a36Sopenharmony_ci * enable/disable the clock line to the card. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP) 53862306a36Sopenharmony_ci val |= UNIPHIER_SD_CLKCTL_OFFEN; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci writel(val, host->ctl + (CTL_SD_CARD_CLK_CTL << 1)); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc, 54462306a36Sopenharmony_ci struct mmc_ios *ios) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct tmio_mmc_host *host = mmc_priv(mmc); 54762306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 54862306a36Sopenharmony_ci struct pinctrl_state *pinstate = NULL; 54962306a36Sopenharmony_ci u32 val, tmp; 55062306a36Sopenharmony_ci bool uhs_en; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci switch (ios->signal_voltage) { 55362306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_330: 55462306a36Sopenharmony_ci val = UNIPHIER_SD_VOLT_330; 55562306a36Sopenharmony_ci uhs_en = false; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_180: 55862306a36Sopenharmony_ci val = UNIPHIER_SD_VOLT_180; 55962306a36Sopenharmony_ci pinstate = priv->pinstate_uhs; 56062306a36Sopenharmony_ci uhs_en = true; 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci default: 56362306a36Sopenharmony_ci return -ENOTSUPP; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci tmp = readl(host->ctl + UNIPHIER_SD_VOLT); 56762306a36Sopenharmony_ci tmp &= ~UNIPHIER_SD_VOLT_MASK; 56862306a36Sopenharmony_ci tmp |= FIELD_PREP(UNIPHIER_SD_VOLT_MASK, val); 56962306a36Sopenharmony_ci writel(tmp, host->ctl + UNIPHIER_SD_VOLT); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (pinstate) 57262306a36Sopenharmony_ci pinctrl_select_state(priv->pinctrl, pinstate); 57362306a36Sopenharmony_ci else 57462306a36Sopenharmony_ci pinctrl_select_default_state(mmc_dev(mmc)); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci uniphier_sd_uhs_enable(host, uhs_en); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int uniphier_sd_uhs_init(struct tmio_mmc_host *host) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct uniphier_sd_priv *priv = uniphier_sd_priv(host); 58462306a36Sopenharmony_ci struct device *dev = &host->pdev->dev; 58562306a36Sopenharmony_ci struct device_node *np = dev->of_node; 58662306a36Sopenharmony_ci struct of_phandle_args args; 58762306a36Sopenharmony_ci int ret; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci priv->pinctrl = devm_pinctrl_get(mmc_dev(host->mmc)); 59062306a36Sopenharmony_ci if (IS_ERR(priv->pinctrl)) 59162306a36Sopenharmony_ci return PTR_ERR(priv->pinctrl); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci priv->pinstate_uhs = pinctrl_lookup_state(priv->pinctrl, "uhs"); 59462306a36Sopenharmony_ci if (IS_ERR(priv->pinstate_uhs)) 59562306a36Sopenharmony_ci return PTR_ERR(priv->pinstate_uhs); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(np, 59862306a36Sopenharmony_ci "socionext,syscon-uhs-mode", 59962306a36Sopenharmony_ci 1, 0, &args); 60062306a36Sopenharmony_ci if (ret) { 60162306a36Sopenharmony_ci dev_err(dev, "Can't get syscon-uhs-mode property\n"); 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci priv->sdctrl_regmap = syscon_node_to_regmap(args.np); 60562306a36Sopenharmony_ci of_node_put(args.np); 60662306a36Sopenharmony_ci if (IS_ERR(priv->sdctrl_regmap)) { 60762306a36Sopenharmony_ci dev_err(dev, "Can't map syscon-uhs-mode\n"); 60862306a36Sopenharmony_ci return PTR_ERR(priv->sdctrl_regmap); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci priv->sdctrl_ch = args.args[0]; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int uniphier_sd_probe(struct platform_device *pdev) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 61862306a36Sopenharmony_ci struct uniphier_sd_priv *priv; 61962306a36Sopenharmony_ci struct tmio_mmc_data *tmio_data; 62062306a36Sopenharmony_ci struct tmio_mmc_host *host; 62162306a36Sopenharmony_ci int irq, ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 62462306a36Sopenharmony_ci if (irq < 0) 62562306a36Sopenharmony_ci return irq; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 62862306a36Sopenharmony_ci if (!priv) 62962306a36Sopenharmony_ci return -ENOMEM; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci priv->caps = (unsigned long)of_device_get_match_data(dev); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci priv->clk = devm_clk_get(dev, NULL); 63462306a36Sopenharmony_ci if (IS_ERR(priv->clk)) { 63562306a36Sopenharmony_ci dev_err(dev, "failed to get clock\n"); 63662306a36Sopenharmony_ci return PTR_ERR(priv->clk); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci priv->rst = devm_reset_control_get_shared(dev, "host"); 64062306a36Sopenharmony_ci if (IS_ERR(priv->rst)) { 64162306a36Sopenharmony_ci dev_err(dev, "failed to get host reset\n"); 64262306a36Sopenharmony_ci return PTR_ERR(priv->rst); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* old version has one more reset */ 64662306a36Sopenharmony_ci if (!(priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP)) { 64762306a36Sopenharmony_ci priv->rst_br = devm_reset_control_get_shared(dev, "bridge"); 64862306a36Sopenharmony_ci if (IS_ERR(priv->rst_br)) { 64962306a36Sopenharmony_ci dev_err(dev, "failed to get bridge reset\n"); 65062306a36Sopenharmony_ci return PTR_ERR(priv->rst_br); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci tmio_data = &priv->tmio_data; 65562306a36Sopenharmony_ci tmio_data->flags |= TMIO_MMC_32BIT_DATA_PORT; 65662306a36Sopenharmony_ci tmio_data->flags |= TMIO_MMC_USE_BUSY_TIMEOUT; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci host = tmio_mmc_host_alloc(pdev, tmio_data); 65962306a36Sopenharmony_ci if (IS_ERR(host)) 66062306a36Sopenharmony_ci return PTR_ERR(host); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (host->mmc->caps & MMC_CAP_HW_RESET) { 66362306a36Sopenharmony_ci priv->rst_hw = devm_reset_control_get_exclusive(dev, "hw"); 66462306a36Sopenharmony_ci if (IS_ERR(priv->rst_hw)) { 66562306a36Sopenharmony_ci dev_err(dev, "failed to get hw reset\n"); 66662306a36Sopenharmony_ci ret = PTR_ERR(priv->rst_hw); 66762306a36Sopenharmony_ci goto free_host; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci host->ops.card_hw_reset = uniphier_sd_hw_reset; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (host->mmc->caps & MMC_CAP_UHS) { 67362306a36Sopenharmony_ci ret = uniphier_sd_uhs_init(host); 67462306a36Sopenharmony_ci if (ret) { 67562306a36Sopenharmony_ci dev_warn(dev, 67662306a36Sopenharmony_ci "failed to setup UHS (error %d). Disabling UHS.", 67762306a36Sopenharmony_ci ret); 67862306a36Sopenharmony_ci host->mmc->caps &= ~MMC_CAP_UHS; 67962306a36Sopenharmony_ci } else { 68062306a36Sopenharmony_ci host->ops.start_signal_voltage_switch = 68162306a36Sopenharmony_ci uniphier_sd_start_signal_voltage_switch; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP) 68662306a36Sopenharmony_ci host->dma_ops = &uniphier_sd_internal_dma_ops; 68762306a36Sopenharmony_ci else 68862306a36Sopenharmony_ci host->dma_ops = &uniphier_sd_external_dma_ops; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci host->bus_shift = 1; 69162306a36Sopenharmony_ci host->clk_enable = uniphier_sd_clk_enable; 69262306a36Sopenharmony_ci host->clk_disable = uniphier_sd_clk_disable; 69362306a36Sopenharmony_ci host->set_clock = uniphier_sd_set_clock; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci ret = uniphier_sd_clk_enable(host); 69662306a36Sopenharmony_ci if (ret) 69762306a36Sopenharmony_ci goto free_host; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci uniphier_sd_host_init(host); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci tmio_data->ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34; 70262306a36Sopenharmony_ci if (host->mmc->caps & MMC_CAP_UHS) 70362306a36Sopenharmony_ci tmio_data->ocr_mask |= MMC_VDD_165_195; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci tmio_data->max_segs = 1; 70662306a36Sopenharmony_ci tmio_data->max_blk_count = U16_MAX; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, TMIO_MASK_ALL); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED, 71162306a36Sopenharmony_ci dev_name(dev), host); 71262306a36Sopenharmony_ci if (ret) 71362306a36Sopenharmony_ci goto disable_clk; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ret = tmio_mmc_host_probe(host); 71662306a36Sopenharmony_ci if (ret) 71762306a36Sopenharmony_ci goto disable_clk; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cidisable_clk: 72262306a36Sopenharmony_ci uniphier_sd_clk_disable(host); 72362306a36Sopenharmony_cifree_host: 72462306a36Sopenharmony_ci tmio_mmc_host_free(host); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic void uniphier_sd_remove(struct platform_device *pdev) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct tmio_mmc_host *host = platform_get_drvdata(pdev); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci tmio_mmc_host_remove(host); 73462306a36Sopenharmony_ci uniphier_sd_clk_disable(host); 73562306a36Sopenharmony_ci tmio_mmc_host_free(host); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic const struct of_device_id uniphier_sd_match[] = { 73962306a36Sopenharmony_ci { 74062306a36Sopenharmony_ci .compatible = "socionext,uniphier-sd-v2.91", 74162306a36Sopenharmony_ci }, 74262306a36Sopenharmony_ci { 74362306a36Sopenharmony_ci .compatible = "socionext,uniphier-sd-v3.1", 74462306a36Sopenharmony_ci .data = (void *)(UNIPHIER_SD_CAP_EXTENDED_IP | 74562306a36Sopenharmony_ci UNIPHIER_SD_CAP_BROKEN_DMA_RX), 74662306a36Sopenharmony_ci }, 74762306a36Sopenharmony_ci { 74862306a36Sopenharmony_ci .compatible = "socionext,uniphier-sd-v3.1.1", 74962306a36Sopenharmony_ci .data = (void *)UNIPHIER_SD_CAP_EXTENDED_IP, 75062306a36Sopenharmony_ci }, 75162306a36Sopenharmony_ci { /* sentinel */ } 75262306a36Sopenharmony_ci}; 75362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, uniphier_sd_match); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic struct platform_driver uniphier_sd_driver = { 75662306a36Sopenharmony_ci .probe = uniphier_sd_probe, 75762306a36Sopenharmony_ci .remove_new = uniphier_sd_remove, 75862306a36Sopenharmony_ci .driver = { 75962306a36Sopenharmony_ci .name = "uniphier-sd", 76062306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 76162306a36Sopenharmony_ci .of_match_table = uniphier_sd_match, 76262306a36Sopenharmony_ci }, 76362306a36Sopenharmony_ci}; 76462306a36Sopenharmony_cimodule_platform_driver(uniphier_sd_driver); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciMODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>"); 76762306a36Sopenharmony_ciMODULE_DESCRIPTION("UniPhier SD/eMMC host controller driver"); 76862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 769