162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale eSDHC ColdFire family controller driver, platform bus. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020 Timesys Corporation 662306a36Sopenharmony_ci * Author: Angelo Dureghello <angelo.dureghello@timesys.it> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/platform_data/mmc-esdhc-mcf.h> 1262306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 1362306a36Sopenharmony_ci#include "sdhci-pltfm.h" 1462306a36Sopenharmony_ci#include "sdhci-esdhc.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define ESDHC_PROCTL_D3CD 0x08 1762306a36Sopenharmony_ci#define ESDHC_SYS_CTRL_DTOCV_MASK 0x0f 1862306a36Sopenharmony_ci#define ESDHC_DEFAULT_HOST_CONTROL 0x28 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Freescale eSDHC has DMA ERR flag at bit 28, not as std spec says, bit 25. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define ESDHC_INT_VENDOR_SPEC_DMA_ERR BIT(28) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct pltfm_mcf_data { 2662306a36Sopenharmony_ci struct clk *clk_ipg; 2762306a36Sopenharmony_ci struct clk *clk_ahb; 2862306a36Sopenharmony_ci struct clk *clk_per; 2962306a36Sopenharmony_ci int aside; 3062306a36Sopenharmony_ci int current_bus_width; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline void esdhc_mcf_buffer_swap32(u32 *buf, int len) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int i; 3662306a36Sopenharmony_ci u32 temp; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci len = (len + 3) >> 2; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 4162306a36Sopenharmony_ci temp = swab32(*buf); 4262306a36Sopenharmony_ci *buf++ = temp; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic inline void esdhc_clrset_be(struct sdhci_host *host, 4762306a36Sopenharmony_ci u32 mask, u32 val, int reg) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci void __iomem *base = host->ioaddr + (reg & ~3); 5062306a36Sopenharmony_ci u8 shift = (reg & 3) << 3; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci mask <<= shift; 5362306a36Sopenharmony_ci val <<= shift; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (reg == SDHCI_HOST_CONTROL) 5662306a36Sopenharmony_ci val |= ESDHC_PROCTL_D3CD; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci writel((readl(base) & ~mask) | val, base); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * Note: mcf is big-endian, single bytes need to be accessed at big endian 6362306a36Sopenharmony_ci * offsets. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistatic void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci void __iomem *base = host->ioaddr + (reg & ~3); 6862306a36Sopenharmony_ci u8 shift = (reg & 3) << 3; 6962306a36Sopenharmony_ci u32 mask = ~(0xff << shift); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (reg == SDHCI_HOST_CONTROL) { 7262306a36Sopenharmony_ci u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL; 7362306a36Sopenharmony_ci u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3; 7462306a36Sopenharmony_ci u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci tmp &= ~0x03; 7762306a36Sopenharmony_ci tmp |= dma_bits; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * Recomposition needed, restore always endianness and 8162306a36Sopenharmony_ci * keep D3CD and AI, just setting bus width. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci host_ctrl |= val; 8462306a36Sopenharmony_ci host_ctrl |= (dma_bits << 8); 8562306a36Sopenharmony_ci writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci writel((readl(base) & mask) | (val << shift), base); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 9662306a36Sopenharmony_ci struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); 9762306a36Sopenharmony_ci void __iomem *base = host->ioaddr + (reg & ~3); 9862306a36Sopenharmony_ci u8 shift = (reg & 3) << 3; 9962306a36Sopenharmony_ci u32 mask = ~(0xffff << shift); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci switch (reg) { 10262306a36Sopenharmony_ci case SDHCI_TRANSFER_MODE: 10362306a36Sopenharmony_ci mcf_data->aside = val; 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci case SDHCI_COMMAND: 10662306a36Sopenharmony_ci if (host->cmd->opcode == MMC_STOP_TRANSMISSION) 10762306a36Sopenharmony_ci val |= SDHCI_CMD_ABORTCMD; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * As for the fsl driver, 11162306a36Sopenharmony_ci * we have to set the mode in a single write here. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci writel(val << 16 | mcf_data->aside, 11462306a36Sopenharmony_ci host->ioaddr + SDHCI_TRANSFER_MODE); 11562306a36Sopenharmony_ci return; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci writel((readl(base) & mask) | (val << shift), base); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci writel(val, host->ioaddr + reg); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (reg == SDHCI_HOST_CONTROL) { 12962306a36Sopenharmony_ci u8 __iomem *base = host->ioaddr + (reg & ~3); 13062306a36Sopenharmony_ci u16 val = readw(base + 2); 13162306a36Sopenharmony_ci u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK; 13262306a36Sopenharmony_ci u8 host_ctrl = val & 0xff; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci host_ctrl &= ~SDHCI_CTRL_DMA_MASK; 13562306a36Sopenharmony_ci host_ctrl |= dma_bits; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return host_ctrl; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return readb(host->ioaddr + (reg ^ 0x3)); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * For SDHCI_HOST_VERSION, sdhci specs defines 0xFE, 14762306a36Sopenharmony_ci * a wrong offset for us, we are at 0xFC. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (reg == SDHCI_HOST_VERSION) 15062306a36Sopenharmony_ci reg -= 2; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return readw(host->ioaddr + (reg ^ 0x2)); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u32 val; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci val = readl(host->ioaddr + reg); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * RM (25.3.9) sd pin clock must never exceed 25Mhz. 16362306a36Sopenharmony_ci * So forcing legacy mode at 25Mhz. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (unlikely(reg == SDHCI_CAPABILITIES)) 16662306a36Sopenharmony_ci val &= ~SDHCI_CAN_DO_HISPD; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (unlikely(reg == SDHCI_INT_STATUS)) { 16962306a36Sopenharmony_ci if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) { 17062306a36Sopenharmony_ci val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR; 17162306a36Sopenharmony_ci val |= SDHCI_INT_ADMA_ERROR; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return val; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic unsigned int esdhc_mcf_get_max_timeout_count(struct sdhci_host *host) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci return 1 << 27; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void esdhc_mcf_set_timeout(struct sdhci_host *host, 18462306a36Sopenharmony_ci struct mmc_command *cmd) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci /* Use maximum timeout counter */ 18762306a36Sopenharmony_ci esdhc_clrset_be(host, ESDHC_SYS_CTRL_DTOCV_MASK, 0xE, 18862306a36Sopenharmony_ci SDHCI_TIMEOUT_CONTROL); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void esdhc_mcf_reset(struct sdhci_host *host, u8 mask) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 19462306a36Sopenharmony_ci struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci sdhci_reset(host, mask); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK, 19962306a36Sopenharmony_ci mcf_data->current_bus_width, SDHCI_HOST_CONTROL); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); 20262306a36Sopenharmony_ci sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic unsigned int esdhc_mcf_pltfm_get_max_clock(struct sdhci_host *host) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return pltfm_host->clock; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic unsigned int esdhc_mcf_pltfm_get_min_clock(struct sdhci_host *host) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return pltfm_host->clock / 256 / 16; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host, 22062306a36Sopenharmony_ci unsigned int clock) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 22362306a36Sopenharmony_ci unsigned long *pll_dr = (unsigned long *)MCF_PLL_DR; 22462306a36Sopenharmony_ci u32 fvco, fsys, fesdhc, temp; 22562306a36Sopenharmony_ci const int sdclkfs[] = {2, 4, 8, 16, 32, 64, 128, 256}; 22662306a36Sopenharmony_ci int delta, old_delta = clock; 22762306a36Sopenharmony_ci int i, q, ri, rq; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (clock == 0) { 23062306a36Sopenharmony_ci host->mmc->actual_clock = 0; 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * ColdFire eSDHC clock.s 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * pll -+-> / outdiv1 --> fsys 23862306a36Sopenharmony_ci * +-> / outdiv3 --> eSDHC clock ---> / SDCCLKFS / DVS 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * mcf5441x datasheet says: 24162306a36Sopenharmony_ci * (8.1.2) eSDHC should be 40 MHz max 24262306a36Sopenharmony_ci * (25.3.9) eSDHC input is, as example, 96 Mhz ... 24362306a36Sopenharmony_ci * (25.3.9) sd pin clock must never exceed 25Mhz 24462306a36Sopenharmony_ci * 24562306a36Sopenharmony_ci * fvco = fsys * outdvi1 + 1 24662306a36Sopenharmony_ci * fshdc = fvco / outdiv3 + 1 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci temp = readl(pll_dr); 24962306a36Sopenharmony_ci fsys = pltfm_host->clock; 25062306a36Sopenharmony_ci fvco = fsys * ((temp & 0x1f) + 1); 25162306a36Sopenharmony_ci fesdhc = fvco / (((temp >> 10) & 0x1f) + 1); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < 8; ++i) { 25462306a36Sopenharmony_ci int result = fesdhc / sdclkfs[i]; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci for (q = 1; q < 17; ++q) { 25762306a36Sopenharmony_ci int finale = result / q; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci delta = abs(clock - finale); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (delta < old_delta) { 26262306a36Sopenharmony_ci old_delta = delta; 26362306a36Sopenharmony_ci ri = i; 26462306a36Sopenharmony_ci rq = q; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Apply divisors and re-enable all the clocks 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci temp = ((sdclkfs[ri] >> 1) << 8) | ((rq - 1) << 4) | 27362306a36Sopenharmony_ci (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN); 27462306a36Sopenharmony_ci esdhc_clrset_be(host, 0x0000fff7, temp, SDHCI_CLOCK_CONTROL); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci host->mmc->actual_clock = clock; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci mdelay(1); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 28462306a36Sopenharmony_ci struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (width) { 28762306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 28862306a36Sopenharmony_ci mcf_data->current_bus_width = ESDHC_CTRL_4BITBUS; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci default: 29162306a36Sopenharmony_ci mcf_data->current_bus_width = 0; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK, 29662306a36Sopenharmony_ci mcf_data->current_bus_width, SDHCI_HOST_CONTROL); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void esdhc_mcf_request_done(struct sdhci_host *host, 30062306a36Sopenharmony_ci struct mmc_request *mrq) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct scatterlist *sg; 30362306a36Sopenharmony_ci u32 *buffer; 30462306a36Sopenharmony_ci int i; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!mrq->data || !mrq->data->bytes_xfered) 30762306a36Sopenharmony_ci goto exit_done; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (mmc_get_dma_dir(mrq->data) != DMA_FROM_DEVICE) 31062306a36Sopenharmony_ci goto exit_done; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * On mcf5441x there is no hw sdma option/flag to select the dma 31462306a36Sopenharmony_ci * transfer endiannes. A swap after the transfer is needed. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) { 31762306a36Sopenharmony_ci buffer = (u32 *)sg_virt(sg); 31862306a36Sopenharmony_ci esdhc_mcf_buffer_swap32(buffer, sg->length); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciexit_done: 32262306a36Sopenharmony_ci mmc_request_done(host->mmc, mrq); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void esdhc_mcf_copy_to_bounce_buffer(struct sdhci_host *host, 32662306a36Sopenharmony_ci struct mmc_data *data, 32762306a36Sopenharmony_ci unsigned int length) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci sg_copy_to_buffer(data->sg, data->sg_len, 33062306a36Sopenharmony_ci host->bounce_buffer, length); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer, 33362306a36Sopenharmony_ci data->blksz * data->blocks); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic struct sdhci_ops sdhci_esdhc_ops = { 33762306a36Sopenharmony_ci .reset = esdhc_mcf_reset, 33862306a36Sopenharmony_ci .set_clock = esdhc_mcf_pltfm_set_clock, 33962306a36Sopenharmony_ci .get_max_clock = esdhc_mcf_pltfm_get_max_clock, 34062306a36Sopenharmony_ci .get_min_clock = esdhc_mcf_pltfm_get_min_clock, 34162306a36Sopenharmony_ci .set_bus_width = esdhc_mcf_pltfm_set_bus_width, 34262306a36Sopenharmony_ci .get_max_timeout_count = esdhc_mcf_get_max_timeout_count, 34362306a36Sopenharmony_ci .set_timeout = esdhc_mcf_set_timeout, 34462306a36Sopenharmony_ci .write_b = esdhc_mcf_writeb_be, 34562306a36Sopenharmony_ci .write_w = esdhc_mcf_writew_be, 34662306a36Sopenharmony_ci .write_l = esdhc_mcf_writel_be, 34762306a36Sopenharmony_ci .read_b = esdhc_mcf_readb_be, 34862306a36Sopenharmony_ci .read_w = esdhc_mcf_readw_be, 34962306a36Sopenharmony_ci .read_l = esdhc_mcf_readl_be, 35062306a36Sopenharmony_ci .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer, 35162306a36Sopenharmony_ci .request_done = esdhc_mcf_request_done, 35262306a36Sopenharmony_ci}; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_esdhc_mcf_pdata = { 35562306a36Sopenharmony_ci .ops = &sdhci_esdhc_ops, 35662306a36Sopenharmony_ci .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_FORCE_DMA, 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * Mandatory quirk, 35962306a36Sopenharmony_ci * controller does not support cmd23, 36062306a36Sopenharmony_ci * without, on > 8G cards cmd23 is used, and 36162306a36Sopenharmony_ci * driver times out. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci SDHCI_QUIRK2_HOST_NO_CMD23, 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int esdhc_mcf_plat_init(struct sdhci_host *host, 36762306a36Sopenharmony_ci struct pltfm_mcf_data *mcf_data) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct mcf_esdhc_platform_data *plat_data; 37062306a36Sopenharmony_ci struct device *dev = mmc_dev(host->mmc); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!dev->platform_data) { 37362306a36Sopenharmony_ci dev_err(dev, "no platform data!\n"); 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci plat_data = (struct mcf_esdhc_platform_data *)dev->platform_data; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* Card_detect */ 38062306a36Sopenharmony_ci switch (plat_data->cd_type) { 38162306a36Sopenharmony_ci default: 38262306a36Sopenharmony_ci case ESDHC_CD_CONTROLLER: 38362306a36Sopenharmony_ci /* We have a working card_detect back */ 38462306a36Sopenharmony_ci host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case ESDHC_CD_PERMANENT: 38762306a36Sopenharmony_ci host->mmc->caps |= MMC_CAP_NONREMOVABLE; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case ESDHC_CD_NONE: 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci switch (plat_data->max_bus_width) { 39462306a36Sopenharmony_ci case 4: 39562306a36Sopenharmony_ci host->mmc->caps |= MMC_CAP_4_BIT_DATA; 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci case 1: 39862306a36Sopenharmony_ci default: 39962306a36Sopenharmony_ci host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int sdhci_esdhc_mcf_probe(struct platform_device *pdev) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct sdhci_host *host; 40962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 41062306a36Sopenharmony_ci struct pltfm_mcf_data *mcf_data; 41162306a36Sopenharmony_ci int err; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, &sdhci_esdhc_mcf_pdata, 41462306a36Sopenharmony_ci sizeof(*mcf_data)); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (IS_ERR(host)) 41762306a36Sopenharmony_ci return PTR_ERR(host); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 42062306a36Sopenharmony_ci mcf_data = sdhci_pltfm_priv(pltfm_host); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci host->sdma_boundary = 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci host->flags |= SDHCI_AUTO_CMD12; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 42762306a36Sopenharmony_ci if (IS_ERR(mcf_data->clk_ipg)) { 42862306a36Sopenharmony_ci err = PTR_ERR(mcf_data->clk_ipg); 42962306a36Sopenharmony_ci goto err_exit; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 43362306a36Sopenharmony_ci if (IS_ERR(mcf_data->clk_ahb)) { 43462306a36Sopenharmony_ci err = PTR_ERR(mcf_data->clk_ahb); 43562306a36Sopenharmony_ci goto err_exit; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mcf_data->clk_per = devm_clk_get(&pdev->dev, "per"); 43962306a36Sopenharmony_ci if (IS_ERR(mcf_data->clk_per)) { 44062306a36Sopenharmony_ci err = PTR_ERR(mcf_data->clk_per); 44162306a36Sopenharmony_ci goto err_exit; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci pltfm_host->clk = mcf_data->clk_per; 44562306a36Sopenharmony_ci pltfm_host->clock = clk_get_rate(pltfm_host->clk); 44662306a36Sopenharmony_ci err = clk_prepare_enable(mcf_data->clk_per); 44762306a36Sopenharmony_ci if (err) 44862306a36Sopenharmony_ci goto err_exit; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci err = clk_prepare_enable(mcf_data->clk_ipg); 45162306a36Sopenharmony_ci if (err) 45262306a36Sopenharmony_ci goto unprep_per; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci err = clk_prepare_enable(mcf_data->clk_ahb); 45562306a36Sopenharmony_ci if (err) 45662306a36Sopenharmony_ci goto unprep_ipg; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci err = esdhc_mcf_plat_init(host, mcf_data); 45962306a36Sopenharmony_ci if (err) 46062306a36Sopenharmony_ci goto unprep_ahb; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci err = sdhci_setup_host(host); 46362306a36Sopenharmony_ci if (err) 46462306a36Sopenharmony_ci goto unprep_ahb; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!host->bounce_buffer) { 46762306a36Sopenharmony_ci dev_err(&pdev->dev, "bounce buffer not allocated"); 46862306a36Sopenharmony_ci err = -ENOMEM; 46962306a36Sopenharmony_ci goto cleanup; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci err = __sdhci_add_host(host); 47362306a36Sopenharmony_ci if (err) 47462306a36Sopenharmony_ci goto cleanup; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cicleanup: 47962306a36Sopenharmony_ci sdhci_cleanup_host(host); 48062306a36Sopenharmony_ciunprep_ahb: 48162306a36Sopenharmony_ci clk_disable_unprepare(mcf_data->clk_ahb); 48262306a36Sopenharmony_ciunprep_ipg: 48362306a36Sopenharmony_ci clk_disable_unprepare(mcf_data->clk_ipg); 48462306a36Sopenharmony_ciunprep_per: 48562306a36Sopenharmony_ci clk_disable_unprepare(mcf_data->clk_per); 48662306a36Sopenharmony_cierr_exit: 48762306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return err; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void sdhci_esdhc_mcf_remove(struct platform_device *pdev) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 49562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 49662306a36Sopenharmony_ci struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci sdhci_remove_host(host, 0); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci clk_disable_unprepare(mcf_data->clk_ipg); 50162306a36Sopenharmony_ci clk_disable_unprepare(mcf_data->clk_ahb); 50262306a36Sopenharmony_ci clk_disable_unprepare(mcf_data->clk_per); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic struct platform_driver sdhci_esdhc_mcf_driver = { 50862306a36Sopenharmony_ci .driver = { 50962306a36Sopenharmony_ci .name = "sdhci-esdhc-mcf", 51062306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 51162306a36Sopenharmony_ci }, 51262306a36Sopenharmony_ci .probe = sdhci_esdhc_mcf_probe, 51362306a36Sopenharmony_ci .remove_new = sdhci_esdhc_mcf_remove, 51462306a36Sopenharmony_ci}; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cimodule_platform_driver(sdhci_esdhc_mcf_driver); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ciMODULE_DESCRIPTION("SDHCI driver for Freescale ColdFire eSDHC"); 51962306a36Sopenharmony_ciMODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>"); 52062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 521