162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) Sunplus Inc. 462306a36Sopenharmony_ci * Author: Tony Huang <tonyhuang.sunplus@gmail.com> 562306a36Sopenharmony_ci * Author: Li-hao Kuo <lhjeff911@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/iopoll.h> 1462306a36Sopenharmony_ci#include <linux/mmc/core.h> 1562306a36Sopenharmony_ci#include <linux/mmc/host.h> 1662306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 1762306a36Sopenharmony_ci#include <linux/mmc/sdio.h> 1862306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/pm.h> 2362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2462306a36Sopenharmony_ci#include <linux/reset.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define SPMMC_MIN_CLK 400000 2762306a36Sopenharmony_ci#define SPMMC_MAX_CLK 52000000 2862306a36Sopenharmony_ci#define SPMMC_MAX_BLK_COUNT 65536 2962306a36Sopenharmony_ci#define SPMMC_MAX_TUNABLE_DLY 7 3062306a36Sopenharmony_ci#define SPMMC_TIMEOUT_US 500000 3162306a36Sopenharmony_ci#define SPMMC_POLL_DELAY_US 10 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define SPMMC_CARD_MEDIATYPE_SRCDST_REG 0x0000 3462306a36Sopenharmony_ci#define SPMMC_MEDIA_TYPE GENMASK(2, 0) 3562306a36Sopenharmony_ci#define SPMMC_DMA_SOURCE GENMASK(6, 4) 3662306a36Sopenharmony_ci#define SPMMC_DMA_DESTINATION GENMASK(10, 8) 3762306a36Sopenharmony_ci#define SPMMC_MEDIA_NONE 0 3862306a36Sopenharmony_ci#define SPMMC_MEDIA_SD 6 3962306a36Sopenharmony_ci#define SPMMC_MEDIA_MS 7 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_0_SIZE_REG 0x0008 4262306a36Sopenharmony_ci#define SPMMC_DMA_BASE_ADDR_REG 0x000C 4362306a36Sopenharmony_ci#define SPMMC_HW_DMA_CTRL_REG 0x0010 4462306a36Sopenharmony_ci#define SPMMC_HW_DMA_RST BIT(9) 4562306a36Sopenharmony_ci#define SPMMC_DMAIDLE BIT(10) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define SPMMC_MAX_DMA_MEMORY_SECTORS 8 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_1_ADDR_REG 0x0018 5062306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_1_LENG_REG 0x001C 5162306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_2_ADDR_REG 0x0020 5262306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_2_LENG_REG 0x0024 5362306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_3_ADDR_REG 0x0028 5462306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_3_LENG_REG 0x002C 5562306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_4_ADDR_REG 0x0030 5662306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_4_LENG_REG 0x0034 5762306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_5_ADDR_REG 0x0038 5862306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_5_LENG_REG 0x003C 5962306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_6_ADDR_REG 0x0040 6062306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_6_LENG_REG 0x0044 6162306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_7_ADDR_REG 0x0048 6262306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_7_LENG_REG 0x004C 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define SPMMC_SD_INT_REG 0x0088 6562306a36Sopenharmony_ci#define SPMMC_SDINT_SDCMPEN BIT(0) 6662306a36Sopenharmony_ci#define SPMMC_SDINT_SDCMP BIT(1) 6762306a36Sopenharmony_ci#define SPMMC_SDINT_SDCMPCLR BIT(2) 6862306a36Sopenharmony_ci#define SPMMC_SDINT_SDIOEN BIT(3) 6962306a36Sopenharmony_ci#define SPMMC_SDINT_SDIO BIT(4) 7062306a36Sopenharmony_ci#define SPMMC_SDINT_SDIOCLR BIT(5) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define SPMMC_SD_PAGE_NUM_REG 0x008C 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define SPMMC_SD_CONFIG0_REG 0x0090 7562306a36Sopenharmony_ci#define SPMMC_SD_PIO_MODE BIT(0) 7662306a36Sopenharmony_ci#define SPMMC_SD_DDR_MODE BIT(1) 7762306a36Sopenharmony_ci#define SPMMC_SD_LEN_MODE BIT(2) 7862306a36Sopenharmony_ci#define SPMMC_SD_TRANS_MODE GENMASK(5, 4) 7962306a36Sopenharmony_ci#define SPMMC_SD_AUTO_RESPONSE BIT(6) 8062306a36Sopenharmony_ci#define SPMMC_SD_CMD_DUMMY BIT(7) 8162306a36Sopenharmony_ci#define SPMMC_SD_RSP_CHK_EN BIT(8) 8262306a36Sopenharmony_ci#define SPMMC_SDIO_MODE BIT(9) 8362306a36Sopenharmony_ci#define SPMMC_SD_MMC_MODE BIT(10) 8462306a36Sopenharmony_ci#define SPMMC_SD_DATA_WD BIT(11) 8562306a36Sopenharmony_ci#define SPMMC_RX4_EN BIT(14) 8662306a36Sopenharmony_ci#define SPMMC_SD_RSP_TYPE BIT(15) 8762306a36Sopenharmony_ci#define SPMMC_MMC8_EN BIT(18) 8862306a36Sopenharmony_ci#define SPMMC_CLOCK_DIVISION GENMASK(31, 20) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define SPMMC_SDIO_CTRL_REG 0x0094 9162306a36Sopenharmony_ci#define SPMMC_INT_MULTI_TRIG BIT(6) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define SPMMC_SD_RST_REG 0x0098 9462306a36Sopenharmony_ci#define SPMMC_SD_CTRL_REG 0x009C 9562306a36Sopenharmony_ci#define SPMMC_NEW_COMMAND_TRIGGER BIT(0) 9662306a36Sopenharmony_ci#define SPMMC_DUMMY_CLOCK_TRIGGER BIT(1) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define SPMMC_SD_STATUS_REG 0x00A0 9962306a36Sopenharmony_ci#define SPMMC_SDSTATUS_DUMMY_READY BIT(0) 10062306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RSP_BUF_FULL BIT(1) 10162306a36Sopenharmony_ci#define SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY BIT(2) 10262306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RX_DATA_BUF_FULL BIT(3) 10362306a36Sopenharmony_ci#define SPMMC_SDSTATUS_CMD_PIN_STATUS BIT(4) 10462306a36Sopenharmony_ci#define SPMMC_SDSTATUS_DAT0_PIN_STATUS BIT(5) 10562306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RSP_TIMEOUT BIT(6) 10662306a36Sopenharmony_ci#define SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT BIT(7) 10762306a36Sopenharmony_ci#define SPMMC_SDSTATUS_STB_TIMEOUT BIT(8) 10862306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RSP_CRC7_ERROR BIT(9) 10962306a36Sopenharmony_ci#define SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR BIT(10) 11062306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RDATA_CRC16_ERROR BIT(11) 11162306a36Sopenharmony_ci#define SPMMC_SDSTATUS_SUSPEND_STATE_READY BIT(12) 11262306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BUSY_CYCLE BIT(13) 11362306a36Sopenharmony_ci#define SPMMC_SDSTATUS_DAT1_PIN_STATUS BIT(14) 11462306a36Sopenharmony_ci#define SPMMC_SDSTATUS_SD_SENSE_STATUS BIT(15) 11562306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BOOT_ACK_TIMEOUT BIT(16) 11662306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BOOT_DATA_TIMEOUT BIT(17) 11762306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BOOT_ACK_ERROR BIT(18) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define SPMMC_SD_STATE_REG 0x00A4 12062306a36Sopenharmony_ci#define SPMMC_CRCTOKEN_CHECK_RESULT GENMASK(6, 4) 12162306a36Sopenharmony_ci#define SPMMC_SDSTATE_ERROR BIT(13) 12262306a36Sopenharmony_ci#define SPMMC_SDSTATE_FINISH BIT(14) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define SPMMC_SD_HW_STATE_REG 0x00A8 12562306a36Sopenharmony_ci#define SPMMC_SD_BLOCKSIZE_REG 0x00AC 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define SPMMC_SD_CONFIG1_REG 0x00B0 12862306a36Sopenharmony_ci#define SPMMC_TX_DUMMY_NUM GENMASK(8, 0) 12962306a36Sopenharmony_ci#define SPMMC_SD_HIGH_SPEED_EN BIT(31) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define SPMMC_SD_TIMING_CONFIG0_REG 0x00B4 13262306a36Sopenharmony_ci#define SPMMC_SD_CLOCK_DELAY GENMASK(2, 0) 13362306a36Sopenharmony_ci#define SPMMC_SD_WRITE_DATA_DELAY GENMASK(6, 4) 13462306a36Sopenharmony_ci#define SPMMC_SD_WRITE_COMMAND_DELAY GENMASK(10, 8) 13562306a36Sopenharmony_ci#define SPMMC_SD_READ_RESPONSE_DELAY GENMASK(14, 12) 13662306a36Sopenharmony_ci#define SPMMC_SD_READ_DATA_DELAY GENMASK(18, 16) 13762306a36Sopenharmony_ci#define SPMMC_SD_READ_CRC_DELAY GENMASK(22, 20) 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define SPMMC_SD_PIODATATX_REG 0x00BC 14062306a36Sopenharmony_ci#define SPMMC_SD_PIODATARX_REG 0x00C0 14162306a36Sopenharmony_ci#define SPMMC_SD_CMDBUF0_3_REG 0x00C4 14262306a36Sopenharmony_ci#define SPMMC_SD_CMDBUF4_REG 0x00C8 14362306a36Sopenharmony_ci#define SPMMC_SD_RSPBUF0_3_REG 0x00CC 14462306a36Sopenharmony_ci#define SPMMC_SD_RSPBUF4_5_REG 0x00D0 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define SPMMC_MAX_RETRIES (8 * 8) 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct spmmc_tuning_info { 14962306a36Sopenharmony_ci int enable_tuning; 15062306a36Sopenharmony_ci int need_tuning; 15162306a36Sopenharmony_ci int retried; /* how many times has been retried */ 15262306a36Sopenharmony_ci u32 rd_crc_dly:3; 15362306a36Sopenharmony_ci u32 rd_dat_dly:3; 15462306a36Sopenharmony_ci u32 rd_rsp_dly:3; 15562306a36Sopenharmony_ci u32 wr_cmd_dly:3; 15662306a36Sopenharmony_ci u32 wr_dat_dly:3; 15762306a36Sopenharmony_ci u32 clk_dly:3; 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define SPMMC_DMA_MODE 0 16162306a36Sopenharmony_ci#define SPMMC_PIO_MODE 1 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistruct spmmc_host { 16462306a36Sopenharmony_ci void __iomem *base; 16562306a36Sopenharmony_ci struct clk *clk; 16662306a36Sopenharmony_ci struct reset_control *rstc; 16762306a36Sopenharmony_ci struct mmc_host *mmc; 16862306a36Sopenharmony_ci struct mmc_request *mrq; /* current mrq */ 16962306a36Sopenharmony_ci int irq; 17062306a36Sopenharmony_ci int dmapio_mode; 17162306a36Sopenharmony_ci struct spmmc_tuning_info tuning_info; 17262306a36Sopenharmony_ci int dma_int_threshold; 17362306a36Sopenharmony_ci int dma_use_int; 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline int spmmc_wait_finish(struct spmmc_host *host) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci u32 state; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, state, 18162306a36Sopenharmony_ci (state & SPMMC_SDSTATE_FINISH), 18262306a36Sopenharmony_ci SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic inline int spmmc_wait_sdstatus(struct spmmc_host *host, unsigned int status_bit) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci u32 status; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return readl_poll_timeout(host->base + SPMMC_SD_STATUS_REG, status, 19062306a36Sopenharmony_ci (status & status_bit), 19162306a36Sopenharmony_ci SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci#define spmmc_wait_rspbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RSP_BUF_FULL) 19562306a36Sopenharmony_ci#define spmmc_wait_rxbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RX_DATA_BUF_FULL) 19662306a36Sopenharmony_ci#define spmmc_wait_txbuf_empty(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY) 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void spmmc_get_rsp(struct spmmc_host *host, struct mmc_command *cmd) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u32 value0_3, value4_5; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!(cmd->flags & MMC_RSP_PRESENT)) 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_136) { 20562306a36Sopenharmony_ci if (spmmc_wait_rspbuf_full(host)) 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); 20862306a36Sopenharmony_ci value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; 20962306a36Sopenharmony_ci cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8); 21062306a36Sopenharmony_ci cmd->resp[1] = value4_5 << 24; 21162306a36Sopenharmony_ci value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); 21262306a36Sopenharmony_ci value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; 21362306a36Sopenharmony_ci cmd->resp[1] |= value0_3 >> 8; 21462306a36Sopenharmony_ci cmd->resp[2] = value0_3 << 24; 21562306a36Sopenharmony_ci cmd->resp[2] |= value4_5 << 8; 21662306a36Sopenharmony_ci value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); 21762306a36Sopenharmony_ci value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; 21862306a36Sopenharmony_ci cmd->resp[2] |= value0_3 >> 24; 21962306a36Sopenharmony_ci cmd->resp[3] = value0_3 << 8; 22062306a36Sopenharmony_ci cmd->resp[3] |= value4_5 >> 8; 22162306a36Sopenharmony_ci } else { 22262306a36Sopenharmony_ci if (spmmc_wait_rspbuf_full(host)) 22362306a36Sopenharmony_ci return; 22462306a36Sopenharmony_ci value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); 22562306a36Sopenharmony_ci value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; 22662306a36Sopenharmony_ci cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8); 22762306a36Sopenharmony_ci cmd->resp[1] = value4_5 << 24; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void spmmc_set_bus_clk(struct spmmc_host *host, int clk) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci unsigned int clkdiv; 23462306a36Sopenharmony_ci int f_min = host->mmc->f_min; 23562306a36Sopenharmony_ci int f_max = host->mmc->f_max; 23662306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (clk < f_min) 23962306a36Sopenharmony_ci clk = f_min; 24062306a36Sopenharmony_ci if (clk > f_max) 24162306a36Sopenharmony_ci clk = f_max; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci clkdiv = (clk_get_rate(host->clk) + clk) / clk - 1; 24462306a36Sopenharmony_ci if (clkdiv > 0xfff) 24562306a36Sopenharmony_ci clkdiv = 0xfff; 24662306a36Sopenharmony_ci value &= ~SPMMC_CLOCK_DIVISION; 24762306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_CLOCK_DIVISION, clkdiv); 24862306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void spmmc_set_bus_timing(struct spmmc_host *host, unsigned int timing) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_CONFIG1_REG); 25462306a36Sopenharmony_ci int clkdiv = FIELD_GET(SPMMC_CLOCK_DIVISION, readl(host->base + SPMMC_SD_CONFIG0_REG)); 25562306a36Sopenharmony_ci int delay = clkdiv / 2 < 7 ? clkdiv / 2 : 7; 25662306a36Sopenharmony_ci int hs_en = 1, ddr_enabled = 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci switch (timing) { 25962306a36Sopenharmony_ci case MMC_TIMING_LEGACY: 26062306a36Sopenharmony_ci hs_en = 0; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case MMC_TIMING_MMC_HS: 26362306a36Sopenharmony_ci case MMC_TIMING_SD_HS: 26462306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR50: 26562306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 26662306a36Sopenharmony_ci case MMC_TIMING_MMC_HS200: 26762306a36Sopenharmony_ci hs_en = 1; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case MMC_TIMING_UHS_DDR50: 27062306a36Sopenharmony_ci ddr_enabled = 1; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case MMC_TIMING_MMC_DDR52: 27362306a36Sopenharmony_ci ddr_enabled = 1; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci default: 27662306a36Sopenharmony_ci hs_en = 0; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (hs_en) { 28162306a36Sopenharmony_ci value |= SPMMC_SD_HIGH_SPEED_EN; 28262306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG1_REG); 28362306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); 28462306a36Sopenharmony_ci value &= ~SPMMC_SD_WRITE_DATA_DELAY; 28562306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY, delay); 28662306a36Sopenharmony_ci value &= ~SPMMC_SD_WRITE_COMMAND_DELAY; 28762306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY, delay); 28862306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci value &= ~SPMMC_SD_HIGH_SPEED_EN; 29162306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG1_REG); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci if (ddr_enabled) { 29462306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_CONFIG0_REG); 29562306a36Sopenharmony_ci value |= SPMMC_SD_DDR_MODE; 29662306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_CONFIG0_REG); 29962306a36Sopenharmony_ci value &= ~SPMMC_SD_DDR_MODE; 30062306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void spmmc_set_bus_width(struct spmmc_host *host, int width) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci switch (width) { 30962306a36Sopenharmony_ci case MMC_BUS_WIDTH_8: 31062306a36Sopenharmony_ci value &= ~SPMMC_SD_DATA_WD; 31162306a36Sopenharmony_ci value |= SPMMC_MMC8_EN; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 31462306a36Sopenharmony_ci value |= SPMMC_SD_DATA_WD; 31562306a36Sopenharmony_ci value &= ~SPMMC_MMC8_EN; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci default: 31862306a36Sopenharmony_ci value &= ~SPMMC_SD_DATA_WD; 31962306a36Sopenharmony_ci value &= ~SPMMC_MMC8_EN; 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * select the working mode of controller: sd/sdio/emmc 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic void spmmc_set_sdmmc_mode(struct spmmc_host *host) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci value |= SPMMC_SD_MMC_MODE; 33362306a36Sopenharmony_ci value &= ~SPMMC_SDIO_MODE; 33462306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void spmmc_sw_reset(struct spmmc_host *host) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci u32 value; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * Must reset dma operation first, or it will 34362306a36Sopenharmony_ci * be stuck on sd_state == 0x1c00 because of 34462306a36Sopenharmony_ci * a controller software reset bug 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci value = readl(host->base + SPMMC_HW_DMA_CTRL_REG); 34762306a36Sopenharmony_ci value |= SPMMC_DMAIDLE; 34862306a36Sopenharmony_ci writel(value, host->base + SPMMC_HW_DMA_CTRL_REG); 34962306a36Sopenharmony_ci value &= ~SPMMC_DMAIDLE; 35062306a36Sopenharmony_ci writel(value, host->base + SPMMC_HW_DMA_CTRL_REG); 35162306a36Sopenharmony_ci value = readl(host->base + SPMMC_HW_DMA_CTRL_REG); 35262306a36Sopenharmony_ci value |= SPMMC_HW_DMA_RST; 35362306a36Sopenharmony_ci writel(value, host->base + SPMMC_HW_DMA_CTRL_REG); 35462306a36Sopenharmony_ci writel(0x7, host->base + SPMMC_SD_RST_REG); 35562306a36Sopenharmony_ci readl_poll_timeout_atomic(host->base + SPMMC_SD_HW_STATE_REG, value, 35662306a36Sopenharmony_ci !(value & BIT(6)), 1, SPMMC_TIMEOUT_US); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void spmmc_prepare_cmd(struct spmmc_host *host, struct mmc_command *cmd) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci u32 value; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* add start bit, according to spec, command format */ 36462306a36Sopenharmony_ci value = ((cmd->opcode | 0x40) << 24) | (cmd->arg >> 8); 36562306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CMDBUF0_3_REG); 36662306a36Sopenharmony_ci writeb(cmd->arg & 0xff, host->base + SPMMC_SD_CMDBUF4_REG); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* disable interrupt if needed */ 36962306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_INT_REG); 37062306a36Sopenharmony_ci value |= SPMMC_SDINT_SDCMPCLR; 37162306a36Sopenharmony_ci value &= ~SPMMC_SDINT_SDCMPEN; 37262306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_INT_REG); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_CONFIG0_REG); 37562306a36Sopenharmony_ci value &= ~SPMMC_SD_TRANS_MODE; 37662306a36Sopenharmony_ci value |= SPMMC_SD_CMD_DUMMY; 37762306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_PRESENT) { 37862306a36Sopenharmony_ci value |= SPMMC_SD_AUTO_RESPONSE; 37962306a36Sopenharmony_ci } else { 38062306a36Sopenharmony_ci value &= ~SPMMC_SD_AUTO_RESPONSE; 38162306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Currently, host is not capable of checking R2's CRC7, 38762306a36Sopenharmony_ci * thus, enable crc7 check only for 48 bit response commands 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_CRC && !(cmd->flags & MMC_RSP_136)) 39062306a36Sopenharmony_ci value |= SPMMC_SD_RSP_CHK_EN; 39162306a36Sopenharmony_ci else 39262306a36Sopenharmony_ci value &= ~SPMMC_SD_RSP_CHK_EN; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_136) 39562306a36Sopenharmony_ci value |= SPMMC_SD_RSP_TYPE; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci value &= ~SPMMC_SD_RSP_TYPE; 39862306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void spmmc_prepare_data(struct spmmc_host *host, struct mmc_data *data) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci u32 value, srcdst; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci writel(data->blocks - 1, host->base + SPMMC_SD_PAGE_NUM_REG); 40662306a36Sopenharmony_ci writel(data->blksz - 1, host->base + SPMMC_SD_BLOCKSIZE_REG); 40762306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_CONFIG0_REG); 40862306a36Sopenharmony_ci if (data->flags & MMC_DATA_READ) { 40962306a36Sopenharmony_ci value &= ~SPMMC_SD_TRANS_MODE; 41062306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 2); 41162306a36Sopenharmony_ci value &= ~SPMMC_SD_AUTO_RESPONSE; 41262306a36Sopenharmony_ci value &= ~SPMMC_SD_CMD_DUMMY; 41362306a36Sopenharmony_ci srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); 41462306a36Sopenharmony_ci srcdst &= ~SPMMC_DMA_SOURCE; 41562306a36Sopenharmony_ci srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x2); 41662306a36Sopenharmony_ci srcdst &= ~SPMMC_DMA_DESTINATION; 41762306a36Sopenharmony_ci srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x1); 41862306a36Sopenharmony_ci writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci value &= ~SPMMC_SD_TRANS_MODE; 42162306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 1); 42262306a36Sopenharmony_ci srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); 42362306a36Sopenharmony_ci srcdst &= ~SPMMC_DMA_SOURCE; 42462306a36Sopenharmony_ci srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x1); 42562306a36Sopenharmony_ci srcdst &= ~SPMMC_DMA_DESTINATION; 42662306a36Sopenharmony_ci srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x2); 42762306a36Sopenharmony_ci writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci value |= SPMMC_SD_LEN_MODE; 43162306a36Sopenharmony_ci if (host->dmapio_mode == SPMMC_DMA_MODE) { 43262306a36Sopenharmony_ci struct scatterlist *sg; 43362306a36Sopenharmony_ci dma_addr_t dma_addr; 43462306a36Sopenharmony_ci unsigned int dma_size; 43562306a36Sopenharmony_ci int i, count = 1; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci count = dma_map_sg(host->mmc->parent, data->sg, data->sg_len, 43862306a36Sopenharmony_ci mmc_get_dma_dir(data)); 43962306a36Sopenharmony_ci if (!count || count > SPMMC_MAX_DMA_MEMORY_SECTORS) { 44062306a36Sopenharmony_ci data->error = -EINVAL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci for_each_sg(data->sg, sg, count, i) { 44562306a36Sopenharmony_ci dma_addr = sg_dma_address(sg); 44662306a36Sopenharmony_ci dma_size = sg_dma_len(sg) / data->blksz - 1; 44762306a36Sopenharmony_ci if (i == 0) { 44862306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_DMA_BASE_ADDR_REG); 44962306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_0_SIZE_REG); 45062306a36Sopenharmony_ci } else if (i == 1) { 45162306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_1_ADDR_REG); 45262306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_1_LENG_REG); 45362306a36Sopenharmony_ci } else if (i == 2) { 45462306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_2_ADDR_REG); 45562306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_2_LENG_REG); 45662306a36Sopenharmony_ci } else if (i == 3) { 45762306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_3_ADDR_REG); 45862306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_3_LENG_REG); 45962306a36Sopenharmony_ci } else if (i == 4) { 46062306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_4_ADDR_REG); 46162306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_4_LENG_REG); 46262306a36Sopenharmony_ci } else if (i == 5) { 46362306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_5_ADDR_REG); 46462306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_5_LENG_REG); 46562306a36Sopenharmony_ci } else if (i == 6) { 46662306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_6_ADDR_REG); 46762306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_6_LENG_REG); 46862306a36Sopenharmony_ci } else if (i == 7) { 46962306a36Sopenharmony_ci writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_7_ADDR_REG); 47062306a36Sopenharmony_ci writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_7_LENG_REG); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci value &= ~SPMMC_SD_PIO_MODE; 47462306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 47562306a36Sopenharmony_ci /* enable interrupt if needed */ 47662306a36Sopenharmony_ci if (data->blksz * data->blocks > host->dma_int_threshold) { 47762306a36Sopenharmony_ci host->dma_use_int = 1; 47862306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_INT_REG); 47962306a36Sopenharmony_ci value &= ~SPMMC_SDINT_SDCMPEN; 48062306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 1); /* sdcmpen */ 48162306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_INT_REG); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci value |= SPMMC_SD_PIO_MODE; 48562306a36Sopenharmony_ci value |= SPMMC_RX4_EN; 48662306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CONFIG0_REG); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic inline void spmmc_trigger_transaction(struct spmmc_host *host) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_CTRL_REG); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci value |= SPMMC_NEW_COMMAND_TRIGGER; 49562306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_CTRL_REG); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic void spmmc_send_stop_cmd(struct spmmc_host *host) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct mmc_command stop = {}; 50162306a36Sopenharmony_ci u32 value; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci stop.opcode = MMC_STOP_TRANSMISSION; 50462306a36Sopenharmony_ci stop.arg = 0; 50562306a36Sopenharmony_ci stop.flags = MMC_RSP_R1B; 50662306a36Sopenharmony_ci spmmc_prepare_cmd(host, &stop); 50762306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_INT_REG); 50862306a36Sopenharmony_ci value &= ~SPMMC_SDINT_SDCMPEN; 50962306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 0); 51062306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_INT_REG); 51162306a36Sopenharmony_ci spmmc_trigger_transaction(host); 51262306a36Sopenharmony_ci readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, value, 51362306a36Sopenharmony_ci (value & SPMMC_SDSTATE_FINISH), 1, SPMMC_TIMEOUT_US); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int spmmc_check_error(struct spmmc_host *host, struct mmc_request *mrq) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int ret = 0; 51962306a36Sopenharmony_ci struct mmc_command *cmd = mrq->cmd; 52062306a36Sopenharmony_ci struct mmc_data *data = mrq->data; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_STATE_REG); 52362306a36Sopenharmony_ci u32 crc_token = FIELD_GET(SPMMC_CRCTOKEN_CHECK_RESULT, value); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (value & SPMMC_SDSTATE_ERROR) { 52662306a36Sopenharmony_ci u32 timing_cfg0 = 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_STATUS_REG); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (host->tuning_info.enable_tuning) { 53162306a36Sopenharmony_ci timing_cfg0 = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); 53262306a36Sopenharmony_ci host->tuning_info.rd_crc_dly = FIELD_GET(SPMMC_SD_READ_CRC_DELAY, 53362306a36Sopenharmony_ci timing_cfg0); 53462306a36Sopenharmony_ci host->tuning_info.rd_dat_dly = FIELD_GET(SPMMC_SD_READ_DATA_DELAY, 53562306a36Sopenharmony_ci timing_cfg0); 53662306a36Sopenharmony_ci host->tuning_info.rd_rsp_dly = FIELD_GET(SPMMC_SD_READ_RESPONSE_DELAY, 53762306a36Sopenharmony_ci timing_cfg0); 53862306a36Sopenharmony_ci host->tuning_info.wr_cmd_dly = FIELD_GET(SPMMC_SD_WRITE_COMMAND_DELAY, 53962306a36Sopenharmony_ci timing_cfg0); 54062306a36Sopenharmony_ci host->tuning_info.wr_dat_dly = FIELD_GET(SPMMC_SD_WRITE_DATA_DELAY, 54162306a36Sopenharmony_ci timing_cfg0); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (value & SPMMC_SDSTATUS_RSP_TIMEOUT) { 54562306a36Sopenharmony_ci ret = -ETIMEDOUT; 54662306a36Sopenharmony_ci host->tuning_info.wr_cmd_dly++; 54762306a36Sopenharmony_ci } else if (value & SPMMC_SDSTATUS_RSP_CRC7_ERROR) { 54862306a36Sopenharmony_ci ret = -EILSEQ; 54962306a36Sopenharmony_ci host->tuning_info.rd_rsp_dly++; 55062306a36Sopenharmony_ci } else if (data) { 55162306a36Sopenharmony_ci if ((value & SPMMC_SDSTATUS_STB_TIMEOUT)) { 55262306a36Sopenharmony_ci ret = -ETIMEDOUT; 55362306a36Sopenharmony_ci host->tuning_info.rd_dat_dly++; 55462306a36Sopenharmony_ci } else if (value & SPMMC_SDSTATUS_RDATA_CRC16_ERROR) { 55562306a36Sopenharmony_ci ret = -EILSEQ; 55662306a36Sopenharmony_ci host->tuning_info.rd_dat_dly++; 55762306a36Sopenharmony_ci } else if (value & SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT) { 55862306a36Sopenharmony_ci ret = -ETIMEDOUT; 55962306a36Sopenharmony_ci host->tuning_info.rd_crc_dly++; 56062306a36Sopenharmony_ci } else if (value & SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR) { 56162306a36Sopenharmony_ci ret = -EILSEQ; 56262306a36Sopenharmony_ci if (crc_token == 0x5) 56362306a36Sopenharmony_ci host->tuning_info.wr_dat_dly++; 56462306a36Sopenharmony_ci else 56562306a36Sopenharmony_ci host->tuning_info.rd_crc_dly++; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci cmd->error = ret; 56962306a36Sopenharmony_ci if (data) { 57062306a36Sopenharmony_ci data->error = ret; 57162306a36Sopenharmony_ci data->bytes_xfered = 0; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci if (!host->tuning_info.need_tuning && host->tuning_info.enable_tuning) 57462306a36Sopenharmony_ci cmd->retries = SPMMC_MAX_RETRIES; 57562306a36Sopenharmony_ci spmmc_sw_reset(host); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (host->tuning_info.enable_tuning) { 57862306a36Sopenharmony_ci timing_cfg0 &= ~SPMMC_SD_READ_CRC_DELAY; 57962306a36Sopenharmony_ci timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, 58062306a36Sopenharmony_ci host->tuning_info.rd_crc_dly); 58162306a36Sopenharmony_ci timing_cfg0 &= ~SPMMC_SD_READ_DATA_DELAY; 58262306a36Sopenharmony_ci timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, 58362306a36Sopenharmony_ci host->tuning_info.rd_dat_dly); 58462306a36Sopenharmony_ci timing_cfg0 &= ~SPMMC_SD_READ_RESPONSE_DELAY; 58562306a36Sopenharmony_ci timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, 58662306a36Sopenharmony_ci host->tuning_info.rd_rsp_dly); 58762306a36Sopenharmony_ci timing_cfg0 &= ~SPMMC_SD_WRITE_COMMAND_DELAY; 58862306a36Sopenharmony_ci timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY, 58962306a36Sopenharmony_ci host->tuning_info.wr_cmd_dly); 59062306a36Sopenharmony_ci timing_cfg0 &= ~SPMMC_SD_WRITE_DATA_DELAY; 59162306a36Sopenharmony_ci timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY, 59262306a36Sopenharmony_ci host->tuning_info.wr_dat_dly); 59362306a36Sopenharmony_ci writel(timing_cfg0, host->base + SPMMC_SD_TIMING_CONFIG0_REG); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci } else if (data) { 59662306a36Sopenharmony_ci data->error = 0; 59762306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci host->tuning_info.need_tuning = ret; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/* 60562306a36Sopenharmony_ci * the strategy is: 60662306a36Sopenharmony_ci * 1. if several continuous delays are acceptable, we choose a middle one; 60762306a36Sopenharmony_ci * 2. otherwise, we choose the first one. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_cistatic inline int spmmc_find_best_delay(u8 candidate_dly) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci int f, w, value; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!candidate_dly) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci f = ffs(candidate_dly) - 1; 61662306a36Sopenharmony_ci w = hweight8(candidate_dly); 61762306a36Sopenharmony_ci value = ((1 << w) - 1) << f; 61862306a36Sopenharmony_ci if (0xff == (value & ~candidate_dly)) 61962306a36Sopenharmony_ci return (f + w / 2); 62062306a36Sopenharmony_ci else 62162306a36Sopenharmony_ci return (f); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void spmmc_xfer_data_pio(struct spmmc_host *host, struct mmc_data *data) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci u32 *buf; 62762306a36Sopenharmony_ci int data_left = data->blocks * data->blksz; 62862306a36Sopenharmony_ci int consumed, remain; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci struct sg_mapping_iter sg_miter; 63162306a36Sopenharmony_ci unsigned int flags = 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) 63462306a36Sopenharmony_ci flags |= SG_MITER_FROM_SG; 63562306a36Sopenharmony_ci else 63662306a36Sopenharmony_ci flags |= SG_MITER_TO_SG; 63762306a36Sopenharmony_ci sg_miter_start(&sg_miter, data->sg, data->sg_len, flags); 63862306a36Sopenharmony_ci while (data_left > 0) { 63962306a36Sopenharmony_ci consumed = 0; 64062306a36Sopenharmony_ci if (!sg_miter_next(&sg_miter)) 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci buf = sg_miter.addr; 64362306a36Sopenharmony_ci remain = sg_miter.length; 64462306a36Sopenharmony_ci do { 64562306a36Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) { 64662306a36Sopenharmony_ci if (spmmc_wait_txbuf_empty(host)) 64762306a36Sopenharmony_ci goto done; 64862306a36Sopenharmony_ci writel(*buf, host->base + SPMMC_SD_PIODATATX_REG); 64962306a36Sopenharmony_ci } else { 65062306a36Sopenharmony_ci if (spmmc_wait_rxbuf_full(host)) 65162306a36Sopenharmony_ci goto done; 65262306a36Sopenharmony_ci *buf = readl(host->base + SPMMC_SD_PIODATARX_REG); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci buf++; 65562306a36Sopenharmony_ci /* tx/rx 4 bytes one time in pio mode */ 65662306a36Sopenharmony_ci consumed += 4; 65762306a36Sopenharmony_ci remain -= 4; 65862306a36Sopenharmony_ci } while (remain); 65962306a36Sopenharmony_ci sg_miter.consumed = consumed; 66062306a36Sopenharmony_ci data_left -= consumed; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_cidone: 66362306a36Sopenharmony_ci sg_miter_stop(&sg_miter); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic void spmmc_controller_init(struct spmmc_host *host) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci u32 value; 66962306a36Sopenharmony_ci int ret = reset_control_assert(host->rstc); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (!ret) { 67262306a36Sopenharmony_ci usleep_range(1000, 1250); 67362306a36Sopenharmony_ci ret = reset_control_deassert(host->rstc); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci value = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); 67762306a36Sopenharmony_ci value &= ~SPMMC_MEDIA_TYPE; 67862306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_MEDIA_TYPE, SPMMC_MEDIA_SD); 67962306a36Sopenharmony_ci writel(value, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/* 68362306a36Sopenharmony_ci * 1. unmap scatterlist if needed; 68462306a36Sopenharmony_ci * 2. get response & check error conditions; 68562306a36Sopenharmony_ci * 3. notify mmc layer the request is done 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_cistatic void spmmc_finish_request(struct spmmc_host *host, struct mmc_request *mrq) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct mmc_command *cmd; 69062306a36Sopenharmony_ci struct mmc_data *data; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (!mrq) 69362306a36Sopenharmony_ci return; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci cmd = mrq->cmd; 69662306a36Sopenharmony_ci data = mrq->data; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (data && SPMMC_DMA_MODE == host->dmapio_mode) { 69962306a36Sopenharmony_ci dma_unmap_sg(host->mmc->parent, data->sg, data->sg_len, mmc_get_dma_dir(data)); 70062306a36Sopenharmony_ci host->dma_use_int = 0; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci spmmc_get_rsp(host, cmd); 70462306a36Sopenharmony_ci spmmc_check_error(host, mrq); 70562306a36Sopenharmony_ci if (mrq->stop) 70662306a36Sopenharmony_ci spmmc_send_stop_cmd(host); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci host->mrq = NULL; 70962306a36Sopenharmony_ci mmc_request_done(host->mmc, mrq); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci/* Interrupt Service Routine */ 71362306a36Sopenharmony_cistatic irqreturn_t spmmc_irq(int irq, void *dev_id) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct spmmc_host *host = dev_id; 71662306a36Sopenharmony_ci u32 value = readl(host->base + SPMMC_SD_INT_REG); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if ((value & SPMMC_SDINT_SDCMP) && (value & SPMMC_SDINT_SDCMPEN)) { 71962306a36Sopenharmony_ci value &= ~SPMMC_SDINT_SDCMPEN; 72062306a36Sopenharmony_ci value |= SPMMC_SDINT_SDCMPCLR; 72162306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_INT_REG); 72262306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci return IRQ_HANDLED; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void spmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct spmmc_host *host = mmc_priv(mmc); 73062306a36Sopenharmony_ci struct mmc_data *data; 73162306a36Sopenharmony_ci struct mmc_command *cmd; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci host->mrq = mrq; 73462306a36Sopenharmony_ci data = mrq->data; 73562306a36Sopenharmony_ci cmd = mrq->cmd; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci spmmc_prepare_cmd(host, cmd); 73862306a36Sopenharmony_ci /* we need manually read response R2. */ 73962306a36Sopenharmony_ci if (cmd->flags & MMC_RSP_136) { 74062306a36Sopenharmony_ci spmmc_trigger_transaction(host); 74162306a36Sopenharmony_ci spmmc_get_rsp(host, cmd); 74262306a36Sopenharmony_ci spmmc_wait_finish(host); 74362306a36Sopenharmony_ci spmmc_check_error(host, mrq); 74462306a36Sopenharmony_ci host->mrq = NULL; 74562306a36Sopenharmony_ci mmc_request_done(host->mmc, mrq); 74662306a36Sopenharmony_ci } else { 74762306a36Sopenharmony_ci if (data) 74862306a36Sopenharmony_ci spmmc_prepare_data(host, data); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (host->dmapio_mode == SPMMC_PIO_MODE && data) { 75162306a36Sopenharmony_ci u32 value; 75262306a36Sopenharmony_ci /* pio data transfer do not use interrupt */ 75362306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_INT_REG); 75462306a36Sopenharmony_ci value &= ~SPMMC_SDINT_SDCMPEN; 75562306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_INT_REG); 75662306a36Sopenharmony_ci spmmc_trigger_transaction(host); 75762306a36Sopenharmony_ci spmmc_xfer_data_pio(host, data); 75862306a36Sopenharmony_ci spmmc_wait_finish(host); 75962306a36Sopenharmony_ci spmmc_finish_request(host, mrq); 76062306a36Sopenharmony_ci } else { 76162306a36Sopenharmony_ci if (host->dma_use_int) { 76262306a36Sopenharmony_ci spmmc_trigger_transaction(host); 76362306a36Sopenharmony_ci } else { 76462306a36Sopenharmony_ci spmmc_trigger_transaction(host); 76562306a36Sopenharmony_ci spmmc_wait_finish(host); 76662306a36Sopenharmony_ci spmmc_finish_request(host, mrq); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic void spmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct spmmc_host *host = (struct spmmc_host *)mmc_priv(mmc); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci spmmc_set_bus_clk(host, ios->clock); 77762306a36Sopenharmony_ci spmmc_set_bus_timing(host, ios->timing); 77862306a36Sopenharmony_ci spmmc_set_bus_width(host, ios->bus_width); 77962306a36Sopenharmony_ci /* ensure mode is correct, because we might have hw reset the controller */ 78062306a36Sopenharmony_ci spmmc_set_sdmmc_mode(host); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* 78462306a36Sopenharmony_ci * Return values for the get_cd callback should be: 78562306a36Sopenharmony_ci * 0 for a absent card 78662306a36Sopenharmony_ci * 1 for a present card 78762306a36Sopenharmony_ci * -ENOSYS when not supported (equal to NULL callback) 78862306a36Sopenharmony_ci * or a negative errno value when something bad happened 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_cistatic int spmmc_get_cd(struct mmc_host *mmc) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci int ret = 0; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (mmc_can_gpio_cd(mmc)) 79562306a36Sopenharmony_ci ret = mmc_gpio_get_cd(mmc); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (ret < 0) 79862306a36Sopenharmony_ci ret = 0; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return ret; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int spmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct spmmc_host *host = mmc_priv(mmc); 80662306a36Sopenharmony_ci u8 smpl_dly = 0, candidate_dly = 0; 80762306a36Sopenharmony_ci u32 value; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci host->tuning_info.enable_tuning = 0; 81062306a36Sopenharmony_ci do { 81162306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); 81262306a36Sopenharmony_ci value &= ~SPMMC_SD_READ_RESPONSE_DELAY; 81362306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly); 81462306a36Sopenharmony_ci value &= ~SPMMC_SD_READ_DATA_DELAY; 81562306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly); 81662306a36Sopenharmony_ci value &= ~SPMMC_SD_READ_CRC_DELAY; 81762306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly); 81862306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (!mmc_send_tuning(mmc, opcode, NULL)) { 82162306a36Sopenharmony_ci candidate_dly |= (1 << smpl_dly); 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci } while (smpl_dly++ <= SPMMC_MAX_TUNABLE_DLY); 82562306a36Sopenharmony_ci host->tuning_info.enable_tuning = 1; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (candidate_dly) { 82862306a36Sopenharmony_ci smpl_dly = spmmc_find_best_delay(candidate_dly); 82962306a36Sopenharmony_ci value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); 83062306a36Sopenharmony_ci value &= ~SPMMC_SD_READ_RESPONSE_DELAY; 83162306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly); 83262306a36Sopenharmony_ci value &= ~SPMMC_SD_READ_DATA_DELAY; 83362306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly); 83462306a36Sopenharmony_ci value &= ~SPMMC_SD_READ_CRC_DELAY; 83562306a36Sopenharmony_ci value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly); 83662306a36Sopenharmony_ci writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return -EIO; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic const struct mmc_host_ops spmmc_ops = { 84462306a36Sopenharmony_ci .request = spmmc_request, 84562306a36Sopenharmony_ci .set_ios = spmmc_set_ios, 84662306a36Sopenharmony_ci .get_cd = spmmc_get_cd, 84762306a36Sopenharmony_ci .execute_tuning = spmmc_execute_tuning, 84862306a36Sopenharmony_ci}; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic irqreturn_t spmmc_func_finish_req(int irq, void *dev_id) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct spmmc_host *host = dev_id; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci spmmc_finish_request(host, host->mrq); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return IRQ_HANDLED; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int spmmc_drv_probe(struct platform_device *pdev) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct mmc_host *mmc; 86262306a36Sopenharmony_ci struct resource *res; 86362306a36Sopenharmony_ci struct spmmc_host *host; 86462306a36Sopenharmony_ci int ret = 0; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct spmmc_host)); 86762306a36Sopenharmony_ci if (!mmc) 86862306a36Sopenharmony_ci return -ENOMEM; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci host = mmc_priv(mmc); 87162306a36Sopenharmony_ci host->mmc = mmc; 87262306a36Sopenharmony_ci host->dmapio_mode = SPMMC_DMA_MODE; 87362306a36Sopenharmony_ci host->dma_int_threshold = 1024; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 87662306a36Sopenharmony_ci if (IS_ERR(host->base)) 87762306a36Sopenharmony_ci return PTR_ERR(host->base); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci host->clk = devm_clk_get(&pdev->dev, NULL); 88062306a36Sopenharmony_ci if (IS_ERR(host->clk)) 88162306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "clk get fail\n"); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci host->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 88462306a36Sopenharmony_ci if (IS_ERR(host->rstc)) 88562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(host->rstc), "rst get fail\n"); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci host->irq = platform_get_irq(pdev, 0); 88862306a36Sopenharmony_ci if (host->irq < 0) 88962306a36Sopenharmony_ci return host->irq; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, host->irq, 89262306a36Sopenharmony_ci spmmc_irq, spmmc_func_finish_req, IRQF_SHARED, 89362306a36Sopenharmony_ci NULL, host); 89462306a36Sopenharmony_ci if (ret) 89562306a36Sopenharmony_ci return ret; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk); 89862306a36Sopenharmony_ci if (ret) 89962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, "failed to enable clk\n"); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci ret = mmc_of_parse(mmc); 90262306a36Sopenharmony_ci if (ret) 90362306a36Sopenharmony_ci goto clk_disable; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci mmc->ops = &spmmc_ops; 90662306a36Sopenharmony_ci mmc->f_min = SPMMC_MIN_CLK; 90762306a36Sopenharmony_ci if (mmc->f_max > SPMMC_MAX_CLK) 90862306a36Sopenharmony_ci mmc->f_max = SPMMC_MAX_CLK; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci ret = mmc_regulator_get_supply(mmc); 91162306a36Sopenharmony_ci if (ret) 91262306a36Sopenharmony_ci goto clk_disable; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (!mmc->ocr_avail) 91562306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 91662306a36Sopenharmony_ci mmc->max_seg_size = SPMMC_MAX_BLK_COUNT * 512; 91762306a36Sopenharmony_ci mmc->max_segs = SPMMC_MAX_DMA_MEMORY_SECTORS; 91862306a36Sopenharmony_ci mmc->max_req_size = SPMMC_MAX_BLK_COUNT * 512; 91962306a36Sopenharmony_ci mmc->max_blk_size = 512; 92062306a36Sopenharmony_ci mmc->max_blk_count = SPMMC_MAX_BLK_COUNT; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, host); 92362306a36Sopenharmony_ci spmmc_controller_init(host); 92462306a36Sopenharmony_ci spmmc_set_sdmmc_mode(host); 92562306a36Sopenharmony_ci host->tuning_info.enable_tuning = 1; 92662306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 92762306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 92862306a36Sopenharmony_ci ret = mmc_add_host(mmc); 92962306a36Sopenharmony_ci if (ret) 93062306a36Sopenharmony_ci goto pm_disable; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cipm_disable: 93562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ciclk_disable: 93862306a36Sopenharmony_ci clk_disable_unprepare(host->clk); 93962306a36Sopenharmony_ci return ret; 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic void spmmc_drv_remove(struct platform_device *dev) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct spmmc_host *host = platform_get_drvdata(dev); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci mmc_remove_host(host->mmc); 94762306a36Sopenharmony_ci pm_runtime_get_sync(&dev->dev); 94862306a36Sopenharmony_ci clk_disable_unprepare(host->clk); 94962306a36Sopenharmony_ci pm_runtime_put_noidle(&dev->dev); 95062306a36Sopenharmony_ci pm_runtime_disable(&dev->dev); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int spmmc_pm_runtime_suspend(struct device *dev) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct spmmc_host *host; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci host = dev_get_drvdata(dev); 95862306a36Sopenharmony_ci clk_disable_unprepare(host->clk); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return 0; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic int spmmc_pm_runtime_resume(struct device *dev) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct spmmc_host *host; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci host = dev_get_drvdata(dev); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return clk_prepare_enable(host->clk); 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(spmmc_pm_ops, spmmc_pm_runtime_suspend, 97362306a36Sopenharmony_ci spmmc_pm_runtime_resume, NULL); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic const struct of_device_id spmmc_of_table[] = { 97662306a36Sopenharmony_ci { 97762306a36Sopenharmony_ci .compatible = "sunplus,sp7021-mmc", 97862306a36Sopenharmony_ci }, 97962306a36Sopenharmony_ci {/* sentinel */} 98062306a36Sopenharmony_ci}; 98162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spmmc_of_table); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic struct platform_driver spmmc_driver = { 98462306a36Sopenharmony_ci .probe = spmmc_drv_probe, 98562306a36Sopenharmony_ci .remove_new = spmmc_drv_remove, 98662306a36Sopenharmony_ci .driver = { 98762306a36Sopenharmony_ci .name = "spmmc", 98862306a36Sopenharmony_ci .pm = pm_ptr(&spmmc_pm_ops), 98962306a36Sopenharmony_ci .of_match_table = spmmc_of_table, 99062306a36Sopenharmony_ci }, 99162306a36Sopenharmony_ci}; 99262306a36Sopenharmony_cimodule_platform_driver(spmmc_driver); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciMODULE_AUTHOR("Tony Huang <tonyhuang.sunplus@gmail.com>"); 99562306a36Sopenharmony_ciMODULE_AUTHOR("Li-hao Kuo <lhjeff911@gmail.com>"); 99662306a36Sopenharmony_ciMODULE_DESCRIPTION("Sunplus MMC controller driver"); 99762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 998