162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Copyright (C) 2019 ASPEED Technology Inc. */ 362306a36Sopenharmony_ci/* Copyright (C) 2019 IBM Corp. */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/clk.h> 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/math64.h> 1062306a36Sopenharmony_ci#include <linux/mmc/host.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "sdhci-pltfm.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define ASPEED_SDC_INFO 0x00 2162306a36Sopenharmony_ci#define ASPEED_SDC_S1_MMC8 BIT(25) 2262306a36Sopenharmony_ci#define ASPEED_SDC_S0_MMC8 BIT(24) 2362306a36Sopenharmony_ci#define ASPEED_SDC_PHASE 0xf4 2462306a36Sopenharmony_ci#define ASPEED_SDC_S1_PHASE_IN GENMASK(25, 21) 2562306a36Sopenharmony_ci#define ASPEED_SDC_S0_PHASE_IN GENMASK(20, 16) 2662306a36Sopenharmony_ci#define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11) 2762306a36Sopenharmony_ci#define ASPEED_SDC_S1_PHASE_IN_EN BIT(10) 2862306a36Sopenharmony_ci#define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8) 2962306a36Sopenharmony_ci#define ASPEED_SDC_S0_PHASE_OUT GENMASK(7, 3) 3062306a36Sopenharmony_ci#define ASPEED_SDC_S0_PHASE_IN_EN BIT(2) 3162306a36Sopenharmony_ci#define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0) 3262306a36Sopenharmony_ci#define ASPEED_SDC_PHASE_MAX 31 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* SDIO{10,20} */ 3562306a36Sopenharmony_ci#define ASPEED_SDC_CAP1_1_8V (0 * 32 + 26) 3662306a36Sopenharmony_ci/* SDIO{14,24} */ 3762306a36Sopenharmony_ci#define ASPEED_SDC_CAP2_SDR104 (1 * 32 + 1) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct aspeed_sdc { 4062306a36Sopenharmony_ci struct clk *clk; 4162306a36Sopenharmony_ci struct resource *res; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci spinlock_t lock; 4462306a36Sopenharmony_ci void __iomem *regs; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct aspeed_sdhci_tap_param { 4862306a36Sopenharmony_ci bool valid; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4) 5162306a36Sopenharmony_ci u8 in; 5262306a36Sopenharmony_ci u8 out; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct aspeed_sdhci_tap_desc { 5662306a36Sopenharmony_ci u32 tap_mask; 5762306a36Sopenharmony_ci u32 enable_mask; 5862306a36Sopenharmony_ci u8 enable_value; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct aspeed_sdhci_phase_desc { 6262306a36Sopenharmony_ci struct aspeed_sdhci_tap_desc in; 6362306a36Sopenharmony_ci struct aspeed_sdhci_tap_desc out; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct aspeed_sdhci_pdata { 6762306a36Sopenharmony_ci unsigned int clk_div_start; 6862306a36Sopenharmony_ci const struct aspeed_sdhci_phase_desc *phase_desc; 6962306a36Sopenharmony_ci size_t nr_phase_descs; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct aspeed_sdhci { 7362306a36Sopenharmony_ci const struct aspeed_sdhci_pdata *pdata; 7462306a36Sopenharmony_ci struct aspeed_sdc *parent; 7562306a36Sopenharmony_ci u32 width_mask; 7662306a36Sopenharmony_ci struct mmc_clk_phase_map phase_map; 7762306a36Sopenharmony_ci const struct aspeed_sdhci_phase_desc *phase_desc; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * The function sets the mirror register for updating 8262306a36Sopenharmony_ci * capbilities of the current slot. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * slot | capability | caps_reg | mirror_reg 8562306a36Sopenharmony_ci * -----|-------------|----------|------------ 8662306a36Sopenharmony_ci * 0 | CAP1_1_8V | SDIO140 | SDIO10 8762306a36Sopenharmony_ci * 0 | CAP2_SDR104 | SDIO144 | SDIO14 8862306a36Sopenharmony_ci * 1 | CAP1_1_8V | SDIO240 | SDIO20 8962306a36Sopenharmony_ci * 1 | CAP2_SDR104 | SDIO244 | SDIO24 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic void aspeed_sdc_set_slot_capability(struct sdhci_host *host, struct aspeed_sdc *sdc, 9262306a36Sopenharmony_ci int capability, bool enable, u8 slot) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci u32 mirror_reg_offset; 9562306a36Sopenharmony_ci u32 cap_val; 9662306a36Sopenharmony_ci u8 cap_reg; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (slot > 1) 9962306a36Sopenharmony_ci return; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci cap_reg = capability / 32; 10262306a36Sopenharmony_ci cap_val = sdhci_readl(host, 0x40 + (cap_reg * 4)); 10362306a36Sopenharmony_ci if (enable) 10462306a36Sopenharmony_ci cap_val |= BIT(capability % 32); 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci cap_val &= ~BIT(capability % 32); 10762306a36Sopenharmony_ci mirror_reg_offset = ((slot + 1) * 0x10) + (cap_reg * 4); 10862306a36Sopenharmony_ci writel(cap_val, sdc->regs + mirror_reg_offset); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc, 11262306a36Sopenharmony_ci struct aspeed_sdhci *sdhci, 11362306a36Sopenharmony_ci bool bus8) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u32 info; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Set/clear 8 bit mode */ 11862306a36Sopenharmony_ci spin_lock(&sdc->lock); 11962306a36Sopenharmony_ci info = readl(sdc->regs + ASPEED_SDC_INFO); 12062306a36Sopenharmony_ci if (bus8) 12162306a36Sopenharmony_ci info |= sdhci->width_mask; 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci info &= ~sdhci->width_mask; 12462306a36Sopenharmony_ci writel(info, sdc->regs + ASPEED_SDC_INFO); 12562306a36Sopenharmony_ci spin_unlock(&sdc->lock); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic u32 12962306a36Sopenharmony_ciaspeed_sdc_set_phase_tap(const struct aspeed_sdhci_tap_desc *desc, 13062306a36Sopenharmony_ci u8 tap, bool enable, u32 reg) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci reg &= ~(desc->enable_mask | desc->tap_mask); 13362306a36Sopenharmony_ci if (enable) { 13462306a36Sopenharmony_ci reg |= tap << __ffs(desc->tap_mask); 13562306a36Sopenharmony_ci reg |= desc->enable_value << __ffs(desc->enable_mask); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return reg; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void 14262306a36Sopenharmony_ciaspeed_sdc_set_phase_taps(struct aspeed_sdc *sdc, 14362306a36Sopenharmony_ci const struct aspeed_sdhci_phase_desc *desc, 14462306a36Sopenharmony_ci const struct aspeed_sdhci_tap_param *taps) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 reg; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spin_lock(&sdc->lock); 14962306a36Sopenharmony_ci reg = readl(sdc->regs + ASPEED_SDC_PHASE); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci reg = aspeed_sdc_set_phase_tap(&desc->in, taps->in, taps->valid, reg); 15262306a36Sopenharmony_ci reg = aspeed_sdc_set_phase_tap(&desc->out, taps->out, taps->valid, reg); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci writel(reg, sdc->regs + ASPEED_SDC_PHASE); 15562306a36Sopenharmony_ci spin_unlock(&sdc->lock); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define PICOSECONDS_PER_SECOND 1000000000000ULL 15962306a36Sopenharmony_ci#define ASPEED_SDHCI_NR_TAPS 15 16062306a36Sopenharmony_ci/* Measured value with *handwave* environmentals and static loading */ 16162306a36Sopenharmony_ci#define ASPEED_SDHCI_MAX_TAP_DELAY_PS 1253 16262306a36Sopenharmony_cistatic int aspeed_sdhci_phase_to_tap(struct device *dev, unsigned long rate_hz, 16362306a36Sopenharmony_ci int phase_deg) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci u64 phase_period_ps; 16662306a36Sopenharmony_ci u64 prop_delay_ps; 16762306a36Sopenharmony_ci u64 clk_period_ps; 16862306a36Sopenharmony_ci unsigned int tap; 16962306a36Sopenharmony_ci u8 inverted; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci phase_deg %= 360; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (phase_deg >= 180) { 17462306a36Sopenharmony_ci inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; 17562306a36Sopenharmony_ci phase_deg -= 180; 17662306a36Sopenharmony_ci dev_dbg(dev, 17762306a36Sopenharmony_ci "Inverting clock to reduce phase correction from %d to %d degrees\n", 17862306a36Sopenharmony_ci phase_deg + 180, phase_deg); 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci inverted = 0; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci prop_delay_ps = ASPEED_SDHCI_MAX_TAP_DELAY_PS / ASPEED_SDHCI_NR_TAPS; 18462306a36Sopenharmony_ci clk_period_ps = div_u64(PICOSECONDS_PER_SECOND, (u64)rate_hz); 18562306a36Sopenharmony_ci phase_period_ps = div_u64((u64)phase_deg * clk_period_ps, 360ULL); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci tap = div_u64(phase_period_ps, prop_delay_ps); 18862306a36Sopenharmony_ci if (tap > ASPEED_SDHCI_NR_TAPS) { 18962306a36Sopenharmony_ci dev_dbg(dev, 19062306a36Sopenharmony_ci "Requested out of range phase tap %d for %d degrees of phase compensation at %luHz, clamping to tap %d\n", 19162306a36Sopenharmony_ci tap, phase_deg, rate_hz, ASPEED_SDHCI_NR_TAPS); 19262306a36Sopenharmony_ci tap = ASPEED_SDHCI_NR_TAPS; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return inverted | tap; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void 19962306a36Sopenharmony_ciaspeed_sdhci_phases_to_taps(struct device *dev, unsigned long rate, 20062306a36Sopenharmony_ci const struct mmc_clk_phase *phases, 20162306a36Sopenharmony_ci struct aspeed_sdhci_tap_param *taps) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci taps->valid = phases->valid; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!phases->valid) 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg); 20962306a36Sopenharmony_ci taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void 21362306a36Sopenharmony_ciaspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps; 21662306a36Sopenharmony_ci struct mmc_clk_phase *params; 21762306a36Sopenharmony_ci struct aspeed_sdhci *sdhci; 21862306a36Sopenharmony_ci struct device *dev; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dev = mmc_dev(host->mmc); 22162306a36Sopenharmony_ci sdhci = sdhci_pltfm_priv(sdhci_priv(host)); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!sdhci->phase_desc) 22462306a36Sopenharmony_ci return; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci params = &sdhci->phase_map.phase[host->timing]; 22762306a36Sopenharmony_ci aspeed_sdhci_phases_to_taps(dev, rate, params, taps); 22862306a36Sopenharmony_ci aspeed_sdc_set_phase_taps(sdhci->parent, sdhci->phase_desc, taps); 22962306a36Sopenharmony_ci dev_dbg(dev, 23062306a36Sopenharmony_ci "Using taps [%d, %d] for [%d, %d] degrees of phase correction at %luHz (%d)\n", 23162306a36Sopenharmony_ci taps->in & ASPEED_SDHCI_NR_TAPS, 23262306a36Sopenharmony_ci taps->out & ASPEED_SDHCI_NR_TAPS, 23362306a36Sopenharmony_ci params->in_deg, params->out_deg, rate, host->timing); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 23962306a36Sopenharmony_ci unsigned long parent, bus; 24062306a36Sopenharmony_ci struct aspeed_sdhci *sdhci; 24162306a36Sopenharmony_ci int div; 24262306a36Sopenharmony_ci u16 clk; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 24562306a36Sopenharmony_ci sdhci = sdhci_pltfm_priv(pltfm_host); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci parent = clk_get_rate(pltfm_host->clk); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (clock == 0) 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (WARN_ON(clock > host->max_clk)) 25562306a36Sopenharmony_ci clock = host->max_clk; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Regarding the AST2600: 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * If (EMMC12C[7:6], EMMC12C[15:8] == 0) then 26162306a36Sopenharmony_ci * period of SDCLK = period of SDMCLK. 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * If (EMMC12C[7:6], EMMC12C[15:8] != 0) then 26462306a36Sopenharmony_ci * period of SDCLK = period of SDMCLK * 2 * (EMMC12C[7:6], EMMC[15:8]) 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * If you keep EMMC12C[7:6] = 0 and EMMC12C[15:8] as one-hot, 26762306a36Sopenharmony_ci * 0x1/0x2/0x4/etc, you will find it is compatible to AST2400 or AST2500 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * Keep the one-hot behaviour for backwards compatibility except for 27062306a36Sopenharmony_ci * supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture 27162306a36Sopenharmony_ci * the 0-value capability in clk_div_start. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) { 27462306a36Sopenharmony_ci bus = parent / div; 27562306a36Sopenharmony_ci if (bus <= clock) 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci div >>= 1; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci clk = div << SDHCI_DIVIDER_SHIFT; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci aspeed_sdhci_configure_phase(host, bus); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci sdhci_enable_clk(host, clk); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic unsigned int aspeed_sdhci_get_max_clock(struct sdhci_host *host) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci if (host->mmc->f_max) 29162306a36Sopenharmony_ci return host->mmc->f_max; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return sdhci_pltfm_clk_get_max_clock(host); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_priv; 29962306a36Sopenharmony_ci struct aspeed_sdhci *aspeed_sdhci; 30062306a36Sopenharmony_ci struct aspeed_sdc *aspeed_sdc; 30162306a36Sopenharmony_ci u8 ctrl; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci pltfm_priv = sdhci_priv(host); 30462306a36Sopenharmony_ci aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv); 30562306a36Sopenharmony_ci aspeed_sdc = aspeed_sdhci->parent; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Set/clear 8-bit mode */ 30862306a36Sopenharmony_ci aspeed_sdc_configure_8bit_mode(aspeed_sdc, aspeed_sdhci, 30962306a36Sopenharmony_ci width == MMC_BUS_WIDTH_8); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Set/clear 1 or 4 bit mode */ 31262306a36Sopenharmony_ci ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 31362306a36Sopenharmony_ci if (width == MMC_BUS_WIDTH_4) 31462306a36Sopenharmony_ci ctrl |= SDHCI_CTRL_4BITBUS; 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci ctrl &= ~SDHCI_CTRL_4BITBUS; 31762306a36Sopenharmony_ci sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic u32 aspeed_sdhci_readl(struct sdhci_host *host, int reg) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci u32 val = readl(host->ioaddr + reg); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (unlikely(reg == SDHCI_PRESENT_STATE) && 32562306a36Sopenharmony_ci (host->mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)) 32662306a36Sopenharmony_ci val ^= SDHCI_CARD_PRESENT; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return val; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic const struct sdhci_ops aspeed_sdhci_ops = { 33262306a36Sopenharmony_ci .read_l = aspeed_sdhci_readl, 33362306a36Sopenharmony_ci .set_clock = aspeed_sdhci_set_clock, 33462306a36Sopenharmony_ci .get_max_clock = aspeed_sdhci_get_max_clock, 33562306a36Sopenharmony_ci .set_bus_width = aspeed_sdhci_set_bus_width, 33662306a36Sopenharmony_ci .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, 33762306a36Sopenharmony_ci .reset = sdhci_reset, 33862306a36Sopenharmony_ci .set_uhs_signaling = sdhci_set_uhs_signaling, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const struct sdhci_pltfm_data aspeed_sdhci_pdata = { 34262306a36Sopenharmony_ci .ops = &aspeed_sdhci_ops, 34362306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev, 34762306a36Sopenharmony_ci struct resource *res) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci resource_size_t delta; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!res || resource_type(res) != IORESOURCE_MEM) 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (res->start < dev->parent->res->start) 35562306a36Sopenharmony_ci return -EINVAL; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci delta = res->start - dev->parent->res->start; 35862306a36Sopenharmony_ci if (delta & (0x100 - 1)) 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return (delta / 0x100) - 1; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int aspeed_sdhci_probe(struct platform_device *pdev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci const struct aspeed_sdhci_pdata *aspeed_pdata; 36762306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 36862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 36962306a36Sopenharmony_ci struct aspeed_sdhci *dev; 37062306a36Sopenharmony_ci struct sdhci_host *host; 37162306a36Sopenharmony_ci struct resource *res; 37262306a36Sopenharmony_ci int slot; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci aspeed_pdata = of_device_get_match_data(&pdev->dev); 37662306a36Sopenharmony_ci if (!aspeed_pdata) { 37762306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing platform configuration data\n"); 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev)); 38262306a36Sopenharmony_ci if (IS_ERR(host)) 38362306a36Sopenharmony_ci return PTR_ERR(host); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 38662306a36Sopenharmony_ci dev = sdhci_pltfm_priv(pltfm_host); 38762306a36Sopenharmony_ci dev->pdata = aspeed_pdata; 38862306a36Sopenharmony_ci dev->parent = dev_get_drvdata(pdev->dev.parent); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 39162306a36Sopenharmony_ci slot = aspeed_sdhci_calculate_slot(dev, res); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (slot < 0) 39462306a36Sopenharmony_ci return slot; 39562306a36Sopenharmony_ci else if (slot >= 2) 39662306a36Sopenharmony_ci return -EINVAL; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (slot < dev->pdata->nr_phase_descs) { 39962306a36Sopenharmony_ci dev->phase_desc = &dev->pdata->phase_desc[slot]; 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci dev_info(&pdev->dev, 40262306a36Sopenharmony_ci "Phase control not supported for slot %d\n", slot); 40362306a36Sopenharmony_ci dev->phase_desc = NULL; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci dev_info(&pdev->dev, "Configured for slot %d\n", slot); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci sdhci_get_of_property(pdev); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (of_property_read_bool(np, "mmc-hs200-1_8v") || 41362306a36Sopenharmony_ci of_property_read_bool(np, "sd-uhs-sdr104")) { 41462306a36Sopenharmony_ci aspeed_sdc_set_slot_capability(host, dev->parent, ASPEED_SDC_CAP1_1_8V, 41562306a36Sopenharmony_ci true, slot); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (of_property_read_bool(np, "sd-uhs-sdr104")) { 41962306a36Sopenharmony_ci aspeed_sdc_set_slot_capability(host, dev->parent, ASPEED_SDC_CAP2_SDR104, 42062306a36Sopenharmony_ci true, slot); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); 42462306a36Sopenharmony_ci if (IS_ERR(pltfm_host->clk)) 42562306a36Sopenharmony_ci return PTR_ERR(pltfm_host->clk); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = clk_prepare_enable(pltfm_host->clk); 42862306a36Sopenharmony_ci if (ret) { 42962306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable SDIO clock\n"); 43062306a36Sopenharmony_ci goto err_pltfm_free; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ret = mmc_of_parse(host->mmc); 43462306a36Sopenharmony_ci if (ret) 43562306a36Sopenharmony_ci goto err_sdhci_add; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (dev->phase_desc) 43862306a36Sopenharmony_ci mmc_of_parse_clk_phase(host->mmc, &dev->phase_map); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ret = sdhci_add_host(host); 44162306a36Sopenharmony_ci if (ret) 44262306a36Sopenharmony_ci goto err_sdhci_add; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cierr_sdhci_add: 44762306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 44862306a36Sopenharmony_cierr_pltfm_free: 44962306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 45062306a36Sopenharmony_ci return ret; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void aspeed_sdhci_remove(struct platform_device *pdev) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 45662306a36Sopenharmony_ci struct sdhci_host *host; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci host = platform_get_drvdata(pdev); 45962306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci sdhci_remove_host(host, 0); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = { 46962306a36Sopenharmony_ci .clk_div_start = 2, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = { 47362306a36Sopenharmony_ci /* SDHCI/Slot 0 */ 47462306a36Sopenharmony_ci [0] = { 47562306a36Sopenharmony_ci .in = { 47662306a36Sopenharmony_ci .tap_mask = ASPEED_SDC_S0_PHASE_IN, 47762306a36Sopenharmony_ci .enable_mask = ASPEED_SDC_S0_PHASE_IN_EN, 47862306a36Sopenharmony_ci .enable_value = 1, 47962306a36Sopenharmony_ci }, 48062306a36Sopenharmony_ci .out = { 48162306a36Sopenharmony_ci .tap_mask = ASPEED_SDC_S0_PHASE_OUT, 48262306a36Sopenharmony_ci .enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN, 48362306a36Sopenharmony_ci .enable_value = 3, 48462306a36Sopenharmony_ci }, 48562306a36Sopenharmony_ci }, 48662306a36Sopenharmony_ci /* SDHCI/Slot 1 */ 48762306a36Sopenharmony_ci [1] = { 48862306a36Sopenharmony_ci .in = { 48962306a36Sopenharmony_ci .tap_mask = ASPEED_SDC_S1_PHASE_IN, 49062306a36Sopenharmony_ci .enable_mask = ASPEED_SDC_S1_PHASE_IN_EN, 49162306a36Sopenharmony_ci .enable_value = 1, 49262306a36Sopenharmony_ci }, 49362306a36Sopenharmony_ci .out = { 49462306a36Sopenharmony_ci .tap_mask = ASPEED_SDC_S1_PHASE_OUT, 49562306a36Sopenharmony_ci .enable_mask = ASPEED_SDC_S1_PHASE_OUT_EN, 49662306a36Sopenharmony_ci .enable_value = 3, 49762306a36Sopenharmony_ci }, 49862306a36Sopenharmony_ci }, 49962306a36Sopenharmony_ci}; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = { 50262306a36Sopenharmony_ci .clk_div_start = 1, 50362306a36Sopenharmony_ci .phase_desc = ast2600_sdhci_phase, 50462306a36Sopenharmony_ci .nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase), 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic const struct of_device_id aspeed_sdhci_of_match[] = { 50862306a36Sopenharmony_ci { .compatible = "aspeed,ast2400-sdhci", .data = &ast2400_sdhci_pdata, }, 50962306a36Sopenharmony_ci { .compatible = "aspeed,ast2500-sdhci", .data = &ast2400_sdhci_pdata, }, 51062306a36Sopenharmony_ci { .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, }, 51162306a36Sopenharmony_ci { } 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic struct platform_driver aspeed_sdhci_driver = { 51562306a36Sopenharmony_ci .driver = { 51662306a36Sopenharmony_ci .name = "sdhci-aspeed", 51762306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 51862306a36Sopenharmony_ci .of_match_table = aspeed_sdhci_of_match, 51962306a36Sopenharmony_ci }, 52062306a36Sopenharmony_ci .probe = aspeed_sdhci_probe, 52162306a36Sopenharmony_ci .remove_new = aspeed_sdhci_remove, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int aspeed_sdc_probe(struct platform_device *pdev) 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct device_node *parent, *child; 52862306a36Sopenharmony_ci struct aspeed_sdc *sdc; 52962306a36Sopenharmony_ci int ret; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL); 53262306a36Sopenharmony_ci if (!sdc) 53362306a36Sopenharmony_ci return -ENOMEM; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci spin_lock_init(&sdc->lock); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci sdc->clk = devm_clk_get(&pdev->dev, NULL); 53862306a36Sopenharmony_ci if (IS_ERR(sdc->clk)) 53962306a36Sopenharmony_ci return PTR_ERR(sdc->clk); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci ret = clk_prepare_enable(sdc->clk); 54262306a36Sopenharmony_ci if (ret) { 54362306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable SDCLK\n"); 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci sdc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &sdc->res); 54862306a36Sopenharmony_ci if (IS_ERR(sdc->regs)) { 54962306a36Sopenharmony_ci ret = PTR_ERR(sdc->regs); 55062306a36Sopenharmony_ci goto err_clk; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, sdc); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci parent = pdev->dev.of_node; 55662306a36Sopenharmony_ci for_each_available_child_of_node(parent, child) { 55762306a36Sopenharmony_ci struct platform_device *cpdev; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci cpdev = of_platform_device_create(child, NULL, &pdev->dev); 56062306a36Sopenharmony_ci if (!cpdev) { 56162306a36Sopenharmony_ci of_node_put(child); 56262306a36Sopenharmony_ci ret = -ENODEV; 56362306a36Sopenharmony_ci goto err_clk; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cierr_clk: 57062306a36Sopenharmony_ci clk_disable_unprepare(sdc->clk); 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic void aspeed_sdc_remove(struct platform_device *pdev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci clk_disable_unprepare(sdc->clk); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic const struct of_device_id aspeed_sdc_of_match[] = { 58262306a36Sopenharmony_ci { .compatible = "aspeed,ast2400-sd-controller", }, 58362306a36Sopenharmony_ci { .compatible = "aspeed,ast2500-sd-controller", }, 58462306a36Sopenharmony_ci { .compatible = "aspeed,ast2600-sd-controller", }, 58562306a36Sopenharmony_ci { } 58662306a36Sopenharmony_ci}; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, aspeed_sdc_of_match); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic struct platform_driver aspeed_sdc_driver = { 59162306a36Sopenharmony_ci .driver = { 59262306a36Sopenharmony_ci .name = "sd-controller-aspeed", 59362306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 59462306a36Sopenharmony_ci .pm = &sdhci_pltfm_pmops, 59562306a36Sopenharmony_ci .of_match_table = aspeed_sdc_of_match, 59662306a36Sopenharmony_ci }, 59762306a36Sopenharmony_ci .probe = aspeed_sdc_probe, 59862306a36Sopenharmony_ci .remove_new = aspeed_sdc_remove, 59962306a36Sopenharmony_ci}; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST) 60262306a36Sopenharmony_ci#include "sdhci-of-aspeed-test.c" 60362306a36Sopenharmony_ci#endif 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int __init aspeed_sdc_init(void) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int rc; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci rc = platform_driver_register(&aspeed_sdhci_driver); 61062306a36Sopenharmony_ci if (rc < 0) 61162306a36Sopenharmony_ci return rc; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci rc = platform_driver_register(&aspeed_sdc_driver); 61462306a36Sopenharmony_ci if (rc < 0) 61562306a36Sopenharmony_ci platform_driver_unregister(&aspeed_sdhci_driver); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return rc; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_cimodule_init(aspeed_sdc_init); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void __exit aspeed_sdc_exit(void) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci platform_driver_unregister(&aspeed_sdc_driver); 62462306a36Sopenharmony_ci platform_driver_unregister(&aspeed_sdhci_driver); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_cimodule_exit(aspeed_sdc_exit); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers"); 62962306a36Sopenharmony_ciMODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); 63062306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 63162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 632