/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt #include #include #include #include #include #include #include #include #include #include #include "mtk_sd.h" #include "dbg.h" #include "include/pmic_regulator.h" #include struct msdc_host *mtk_msdc_host[HOST_MAX_NUM]; EXPORT_SYMBOL(mtk_msdc_host); int g_dma_debug[HOST_MAX_NUM]; u32 latest_int_status[HOST_MAX_NUM]; unsigned int msdc_latest_transfer_mode[HOST_MAX_NUM] = { /* 0 for PIO; 1 for DMA; 3 for nothing */ MODE_NONE, MODE_NONE }; unsigned int msdc_latest_op[HOST_MAX_NUM] = { /* 0 for read; 1 for write; 2 for nothing */ OPER_TYPE_NUM, OPER_TYPE_NUM }; /* for debug zone */ unsigned int sd_debug_zone[HOST_MAX_NUM] = { 0, 0 }; /* for enable/disable register dump */ unsigned int sd_register_zone[HOST_MAX_NUM] = { 1, 1 }; /* mode select */ u32 dma_size[HOST_MAX_NUM] = { 512, 512 }; u32 drv_mode[HOST_MAX_NUM] = { MODE_SIZE_DEP, /* using DMA or not depend on the size */ MODE_SIZE_DEP }; int dma_force[HOST_MAX_NUM]; /* used for sd ioctrol */ /**************************************************************/ /* Section 1: Device Tree Global Variables */ /**************************************************************/ const struct of_device_id msdc_of_ids[] = { { .compatible = DT_COMPATIBLE_NAME, }, { }, }; #if !defined(FPGA_PLATFORM) static void __iomem *gpio_base; static void __iomem *topckgen_base; static void __iomem *infracfg_ao_base; static void __iomem *apmixed_base; #endif void __iomem *msdc_io_cfg_bases[HOST_MAX_NUM]; /**************************************************************/ /* Section 2: Power */ /**************************************************************/ #if !defined(FPGA_PLATFORM) int msdc_regulator_set_and_enable(struct regulator *reg, int powerVolt) { #ifndef CONFIG_MTK_MSDC_BRING_UP_BYPASS regulator_set_voltage(reg, powerVolt, powerVolt); return regulator_enable(reg); #else return 0; #endif } void msdc_ldo_power(u32 on, struct regulator *reg, int voltage_mv, u32 *status) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) int voltage_uv = voltage_mv * 1000; if (reg == NULL) return; if (on) { /* want to power on */ if (*status == 0) { /* can power on */ /* Comment out to reduce log */ /* pr_notice("msdc power on<%d>\n", voltage_uv); */ (void)msdc_regulator_set_and_enable(reg, voltage_uv); *status = voltage_uv; } else if (*status == voltage_uv) { pr_notice("msdc power on <%d> again!\n", voltage_uv); } else { pr_notice("msdc change<%d> to <%d>\n", *status, voltage_uv); regulator_disable(reg); (void)msdc_regulator_set_and_enable(reg, voltage_uv); *status = voltage_uv; } } else { /* want to power off */ if (*status != 0) { /* has been powerred on */ pr_notice("msdc power off\n"); (void)regulator_disable(reg); *status = 0; } else { pr_notice("msdc not power on\n"); } } #endif } void msdc_dump_ldo_sts(char **buff, unsigned long *size, struct seq_file *m, struct msdc_host *host) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) \ || defined(MTK_MSDC_BRINGUP_DEBUG) u32 ldo_en = 0, vm_mode = 0; u32 ldo_vol[2] = {0}, ldo_cal[2] = {0}; u32 id = host->id; switch (id) { case 0: /* * 6359p provide two groups of VOSEL and VOCAL * when ufs3.0 used,you need to check VOSEL_1, * or you need to check VOSEL_0 * you can identify the right VOSEL by checking VM_MODE * VM_MODE=1 <-->VOSEL_1 * VM_MODE=0 <-->VOSEL_0 */ pmic_read_interface_nolock(REG_VEMC_EN, &ldo_en, MASK_VEMC_EN, SHIFT_VEMC_EN); pmic_read_interface_nolock(PMIC_RG_VEMC_VOSEL_0_ADDR, &ldo_vol[0], PMIC_RG_VEMC_VOSEL_0_MASK, PMIC_RG_VEMC_VOSEL_0_SHIFT); pmic_read_interface_nolock(PMIC_RG_VEMC_VOCAL_0_ADDR, &ldo_cal[0], PMIC_RG_VEMC_VOCAL_0_MASK, PMIC_RG_VEMC_VOCAL_0_SHIFT); pmic_read_interface_nolock(PMIC_RG_VEMC_VOSEL_1_ADDR, &ldo_vol[1], PMIC_RG_VEMC_VOSEL_1_MASK, PMIC_RG_VEMC_VOSEL_1_SHIFT); pmic_read_interface_nolock(PMIC_RG_VEMC_VOCAL_1_ADDR, &ldo_cal[1], PMIC_RG_VEMC_VOCAL_1_MASK, PMIC_RG_VEMC_VOCAL_1_SHIFT); pmic_read_interface_nolock(PMIC_VM_MODE_ADDR, &vm_mode, PMIC_VM_MODE_MASK, PMIC_VM_MODE_SHIFT); SPREAD_PRINTF(buff, size, m, " VEMC_EN=0x%x, VM_MODE=%d, VEMC_VOL=0:0x%x 1:0x%x [4b'1011(3V)], VEMC_CAL=0:0x%x 1:0x%x\n", ldo_en, vm_mode, ldo_vol[0], ldo_vol[1], ldo_cal[0], ldo_cal[1]); break; case 1: /* * 6360 only provide regulator APIs with mutex protection. * Therefore, can not dump msdc1 ldo status in IRQ context. * Enable dump msdc1 if you make sure the dump context * is correct. */ #if 0 SPREAD_PRINTF(buff, size, m, " VMCH_EN=0x%x, VMCH_VOL=0x%x\n", regulator_is_enabled(host->mmc->supply.vmmc), regulator_get_voltage(host->mmc->supply.vmmc)); SPREAD_PRINTF(buff, size, m, " VMC_EN=0x%x, VMC_VOL=0x%x\n", regulator_is_enabled(host->mmc->supply.vqmmc), regulator_get_voltage(host->mmc->supply.vqmmc)); break; #endif default: break; } #endif } void msdc_sd_power_switch(struct msdc_host *host, u32 on) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) if (host->id == 1) { msdc_ldo_power(on, host->mmc->supply.vqmmc, VOL_1860, &host->power_io); msdc_set_tdsel(host, MSDC_TDRDSEL_CUST, 0); msdc_set_rdsel(host, MSDC_TDRDSEL_CUST, 0); host->hw->driving_applied = &host->hw->driving_sdr50; msdc_set_driving(host, host->hw->driving_applied); } #endif } void msdc_sdio_power(struct msdc_host *host, u32 on) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) if (host->id == 2) { host->power_flash = VOL_1800 * 1000; host->power_io = VOL_1800 * 1000; } #endif } void msdc_power_calibration_init(struct msdc_host *host) { #if 0 if (host->hw->host_function == MSDC_EMMC) { pmic_config_interface(REG_VEMC_VOSEL_CAL, VEMC_VOSEL_CAL_mV(EMMC_VOL_ACTUAL - VOL_3000), MASK_VEMC_VOSEL_CAL, SHIFT_VEMC_VOSEL_CAL); } else if (host->hw->host_function == MSDC_SD) { #if 0 pmic_config_interface(REG_VMCH_VOSEL_CAL, VMCH_VOSEL_CAL_mV(SD_VOL_ACTUAL - VOL_3000), MASK_VMCH_VOSEL_CAL, SHIFT_VMCH_VOSEL_CAL); pmic_config_interface(REG_VMC_VOSEL_CAL, VMC_VOSEL_CAL_mV(SD_VOL_ACTUAL - VOL_3000), MASK_VMC_VOSEL_CAL, SHIFT_VMC_VOSEL_CAL); #endif } #endif } int msdc_oc_check(struct msdc_host *host, u32 en) { /* oc is detected by PMIc interrupt */ return 0; } void msdc_emmc_power(struct msdc_host *host, u32 on) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) if (on) { msdc_set_driving(host, &host->hw->driving); msdc_set_tdsel(host, MSDC_TDRDSEL_CUST, 0); msdc_set_rdsel(host, MSDC_TDRDSEL_CUST, 0); } msdc_ldo_power(on, host->mmc->supply.vmmc, VOL_3000, &host->power_flash); pr_info("msdc%d power %s\n", host->id, (on ? "on" : "off")); #endif #ifdef MTK_MSDC_BRINGUP_DEBUG msdc_dump_ldo_sts(NULL, 0, NULL, host); #endif } struct reg_oc_msdc { struct notifier_block nb; struct work_struct work; }; static struct reg_oc_msdc sd_oc; void msdc_sd_power(struct msdc_host *host, u32 on) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) u32 card_on = on; switch (host->id) { case 1: msdc_set_driving(host, &host->hw->driving); msdc_set_tdsel(host, MSDC_TDRDSEL_CUST, 0); msdc_set_rdsel(host, MSDC_TDRDSEL_CUST, 0); if (host->hw->flags & MSDC_SD_NEED_POWER) card_on = 1; /* soft start, when power on */ if (card_on) { #if 0 /* * 2'b00: 60us * 2'b01: 120 us * 2'b10: 240 us * 2'b11: 360 us */ pmic_set_register_value(PMIC_RG_LDO_VMCH_STBTD, 0x2); /* * 2'b00: 60us * 2'b01: 120 us * 2'b10: 180 us * 2'b11: 240 us */ pmic_set_register_value(PMIC_RG_LDO_VMC_STBTD, 0x3); #endif } /* Disable VMCH/VMC OC */ if (!card_on) { devm_regulator_unregister_notifier( host->mmc->supply.vmmc, &sd_oc.nb); devm_regulator_unregister_notifier( host->mmc->supply.vqmmc, &sd_oc.nb); } /* VMCH VOLSEL */ msdc_ldo_power(card_on, host->mmc->supply.vmmc, VOL_3000, &host->power_flash); /* VMC VOLSEL */ msdc_ldo_power(on, host->mmc->supply.vqmmc, VOL_3000, &host->power_io); /* Enable VMCH/VMC OC */ if (card_on) { mdelay(3); devm_regulator_register_notifier( host->mmc->supply.vmmc, &sd_oc.nb); devm_regulator_register_notifier( host->mmc->supply.vqmmc, &sd_oc.nb); } pr_info("msdc%d power %s\n", host->id, (on ? "on" : "off")); break; default: break; } #ifdef MTK_MSDC_BRINGUP_DEBUG msdc_dump_ldo_sts(NULL, 0, NULL, host); #endif #endif } static int msdc_sd_event(struct notifier_block *nb, unsigned long event, void *data) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) switch (event) { case REGULATOR_EVENT_OVER_CURRENT: case REGULATOR_EVENT_FAIL: schedule_work(&sd_oc.work); break; default: break; }; #endif return NOTIFY_OK; } static void sdcard_oc_handler(struct work_struct *work) { msdc_sd_power_off(); } void msdc_sd_power_off(void) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) struct msdc_host *host = mtk_msdc_host[1]; if (host) { pr_notice("Power Off, SD card\n"); /* power must be on */ host->power_io = VOL_3000 * 1000; host->power_flash = VOL_3000 * 1000; host->power_control(host, 0); msdc_set_bad_card_and_remove(host); } #endif } EXPORT_SYMBOL(msdc_sd_power_off); void msdc_dump_vcore(char **buff, unsigned long *size, struct seq_file *m) { #if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) && defined(VCOREFS_READY) SPREAD_PRINTF(buff, size, m, "%s: Vcore %d\n", __func__, get_cur_vcore_opp()); #endif } void msdc_dump_dvfs_reg(char **buff, unsigned long *size, struct seq_file *m, struct msdc_host *host) { } void msdc_pmic_force_vcore_pwm(bool enable) { /* Temporarily disable force pwm */ /* buck_set_mode(VCORE, enable); */ } #endif /*if !defined(FPGA_PLATFORM)*/ void msdc_set_host_power_control(struct msdc_host *host) { if (host->hw->host_function == MSDC_EMMC) { host->power_control = msdc_emmc_power; } else if (host->hw->host_function == MSDC_SD) { host->power_control = msdc_sd_power; host->power_switch = msdc_sd_power_switch; #if SD_POWER_DEFAULT_ON /* If SD card power is default on, turn it off so that * removable card slot won't keep power when no card plugged */ if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) { /* turn on first to match HW/SW state*/ msdc_sd_power(host, 1); mdelay(10); msdc_sd_power(host, 0); } #endif } else if (host->hw->host_function == MSDC_SDIO) { host->power_control = msdc_sdio_power; } if (host->power_control != NULL) { msdc_power_calibration_init(host); } else { ERR_MSG("Host function defination error for msdc%d", host->id); WARN_ON(1); } } #if defined(MSDC_HQA) //#define MSDC_HQA_HV /*#define MSDC_HQA_NV*/ //#define MSDC_HQA_LV void msdc_HQA_set_voltage(struct msdc_host *host) { #if defined(MSDC_HQA_HV) || defined(MSDC_HQA_LV) u32 vcore; /*u32 val_delta;*/ #endif u32 vio_cal = 0, vio_trim = 0, vio_sel = 0; static int vcore_orig = -1; if (host->is_autok_done == 1) return; if (vcore_orig < 0) pmic_read_interface(0x1534, &vcore_orig, 0x7F, 0); pmic_read_interface(PMIC_RG_VIO18_VOCAL_ADDR, &vio_cal, PMIC_RG_VIO18_VOCAL_MASK, PMIC_RG_VIO18_VOCAL_SHIFT); pmic_read_interface(PMIC_RG_VIO18_VOTRIM_ADDR, &vio_trim, PMIC_RG_VIO18_VOTRIM_MASK, PMIC_RG_VIO18_VOTRIM_SHIFT); pmic_read_interface(PMIC_RG_VIO18_VOSEL_ADDR, &vio_sel, PMIC_RG_VIO18_VOSEL_MASK, PMIC_RG_VIO18_VOSEL_SHIFT); pr_info("[MSDC%d HQA] orig Vcore 0x%x, Vio_Sel=0x%x, Vio_trim 0x%x, Vio_cal 0x%x\n", host->id, vcore_orig, vio_sel, vio_trim, vio_cal); #if defined(MSDC_HQA_HV) || defined(MSDC_HQA_LV) /*val_delta = (500000 + vcore_orig * 6250) / 20 / 6250;*/ vcore = vcore_orig; vio_cal = 0; vio_trim = 0; #ifdef MSDC_HQA_HV /*vcore = vcore_orig + val_delta;*/ /* set to 1.89V */ vio_sel = 0xC; vio_cal = 0x9; /*+90mV*/ vio_trim = 0; #endif #ifdef MSDC_HQA_LV /*vcore = vcore_orig - val_delta;*/ /* set to 1.71V */ vio_sel = 0xB; vio_cal = 0x1; /*+10mv*/ vio_trim = 0; #endif /*let DRAM do the setting for Vcore*/ /*pmic_config_interface(0x14aa, vcore, 0x7F, 0);*/ pr_info("[MSDC%d HQA] adj Vcore 0x%x, Vio_sel=%x, Vio_trim 0x%x, Vio_cal= 0x%x(0x9 = +90mv)\n", host->id, vcore, vio_sel, vio_trim, vio_cal); pmic_config_interface(PMIC_RG_VIO18_VOCAL_ADDR, vio_cal, PMIC_RG_VIO18_VOCAL_MASK, PMIC_RG_VIO18_VOCAL_SHIFT); /* trim need ask pmic owner if trim efuse reg */ pmic_config_interface(PMIC_TMA_KEY_ADDR, 0x9CA6, PMIC_TMA_KEY_MASK, PMIC_TMA_KEY_SHIFT); pmic_config_interface(PMIC_RG_VIO18_VOTRIM_ADDR, vio_trim, PMIC_RG_VIO18_VOTRIM_MASK, PMIC_RG_VIO18_VOTRIM_SHIFT); pmic_config_interface(PMIC_TMA_KEY_ADDR, 0x0, PMIC_TMA_KEY_MASK, PMIC_TMA_KEY_SHIFT); /*read twice to check*/ pmic_read_interface(PMIC_RG_VIO18_VOCAL_ADDR, &vio_cal, PMIC_RG_VIO18_VOCAL_MASK, PMIC_RG_VIO18_VOCAL_SHIFT); pmic_read_interface(PMIC_RG_VIO18_VOTRIM_ADDR, &vio_trim, PMIC_RG_VIO18_VOTRIM_MASK, PMIC_RG_VIO18_VOTRIM_SHIFT); pmic_read_interface(PMIC_RG_VIO18_VOSEL_ADDR, &vio_sel, PMIC_RG_VIO18_VOSEL_MASK, PMIC_RG_VIO18_VOSEL_SHIFT); pr_info("[MSDC%d HQA] after set: Vcore 0x%x,Vio_sel=0x%x,Vio_trim 0x%x, Vio_cal 0x%x\n", host->id, vcore_orig, vio_sel, vio_trim, vio_cal); #endif } #endif /**************************************************************/ /* Section 3: Clock */ /**************************************************************/ #if !defined(FPGA_PLATFORM) u32 hclks_msdc0[] = { MSDC0_SRC_0, MSDC0_SRC_1, MSDC0_SRC_2, MSDC0_SRC_3, MSDC0_SRC_4, MSDC0_SRC_5}; /* msdc1/2 clock source reference value is 200M */ u32 hclks_msdc1[] = { MSDC1_SRC_0, MSDC1_SRC_1, MSDC1_SRC_2, MSDC1_SRC_3, MSDC1_SRC_4}; u32 *hclks_msdc_all[] = { hclks_msdc0, hclks_msdc1, }; u32 *hclks_msdc; int msdc_get_ccf_clk_pointer(struct platform_device *pdev, struct msdc_host *host) { u32 clk_freq; static char const * const clk_names[] = { MSDC0_CLK_NAME, MSDC1_CLK_NAME }; static char const * const hclk_names[] = { MSDC0_HCLK_NAME, MSDC1_HCLK_NAME }; if (clk_names[pdev->id]) { host->clk_ctl = devm_clk_get(&pdev->dev, clk_names[pdev->id]); if (IS_ERR(host->clk_ctl)) { pr_notice("[msdc%d] cannot get clk ctrl\n", pdev->id); return 1; } if (clk_prepare_enable(host->clk_ctl)) { pr_notice("[msdc%d] cannot prepare clk ctrl\n", pdev->id); return 1; } } if (hclk_names[pdev->id]) { host->hclk_ctl = devm_clk_get(&pdev->dev, hclk_names[pdev->id]); if (IS_ERR(host->hclk_ctl)) { pr_notice("[msdc%d] cannot get hclk ctrl\n", pdev->id); return 1; } if (clk_prepare_enable(host->hclk_ctl)) { pr_notice("[msdc%d] cannot prepare hclk ctrl\n", pdev->id); return 1; } } if (host->clk_ctl) { clk_freq = clk_get_rate(host->clk_ctl); if (clk_freq > 0) host->hclk = clk_freq; } #if defined(CONFIG_MTK_HW_FDE) || defined(CONFIG_MMC_CRYPTO) if (pdev->id == 0) { host->aes_clk_ctl = devm_clk_get(&pdev->dev, MSDC0_AES_CLK_NAME); if (IS_ERR(host->aes_clk_ctl)) { pr_notice("[msdc%d] can not get aes clock control\n", pdev->id); WARN_ON(1); return 1; } if (clk_prepare_enable(host->aes_clk_ctl)) { pr_notice( "[msdc%d] can not prepare aes clock control\n", pdev->id); WARN_ON(1); return 1; } } #endif pr_info("[msdc%d] hclk:%d, clk_ctl:%p, hclk_ctl:%p\n", pdev->id, host->hclk, host->clk_ctl, host->hclk_ctl); return 0; } #include static void msdc_dump_clock_sts_core(char **buff, unsigned long *size, struct seq_file *m, struct msdc_host *host) { char buffer[512]; char *buf_ptr = buffer; if (topckgen_base && infracfg_ao_base) { buf_ptr += sprintf(buf_ptr, "MSDC0 HCLK_MUX[0x%p][1:0]=%d, pdn=%d\n", topckgen_base + 0x080, /* mux at bits 1~0 */ (MSDC_READ32(topckgen_base + 0x080) >> 0) & 3, /* pdn at bit 7 */ (MSDC_READ32(topckgen_base + 0x080) >> 7) & 1); buf_ptr += sprintf(buf_ptr, "MSDC0 CLK_MUX[%p][10:8]=%d, pdn=%d, CLK_CG[%p]bit 2,6=%d,%d\n", topckgen_base + 0x080, /* mux at bits 10~8 */ (MSDC_READ32(topckgen_base + 0x080) >> 8) & 7, /* pdn at bit 15 */ (MSDC_READ32(topckgen_base + 0x080) >> 15) & 1, infracfg_ao_base + 0x094, /* cg at bit 2,6 */ (MSDC_READ32(infracfg_ao_base + 0x094) >> 2) & 1, (MSDC_READ32(infracfg_ao_base + 0x094) >> 6) & 1); buf_ptr += sprintf(buf_ptr, "MSDC1 CLK_MUX[%p][18:16]=%d, pdn=%d, CLK_CG[%p]bit 4,16=%d,%d\n", topckgen_base + 0x080, /* mux at bits 18~16 */ (MSDC_READ32(topckgen_base + 0x080) >> 16) & 7, /* pdn at bit 23 */ (MSDC_READ32(topckgen_base + 0x080) >> 23) & 1, infracfg_ao_base + 0x094, /* cg at bit 4,16 */ (MSDC_READ32(infracfg_ao_base + 0x094) >> 4) & 1, (MSDC_READ32(infracfg_ao_base + 0x094) >> 16) & 1); *buf_ptr = '\0'; SPREAD_PRINTF(buff, size, m, "%s", buffer); } buf_ptr = buffer; if (apmixed_base) { /* bit0 is enables PLL, 0: disable 1: enable */ buf_ptr += sprintf(buf_ptr, "MSDCPLL_CON0@0x%p = 0x%x, bit[0] shall 1b\n", apmixed_base + MSDCPLL_CON0_OFFSET, MSDC_READ32(apmixed_base + MSDCPLL_CON0_OFFSET)); buf_ptr += sprintf(buf_ptr, "MSDCPLL_CON1@0x%p = 0x%x\n", apmixed_base + MSDCPLL_CON1_OFFSET, MSDC_READ32(apmixed_base + MSDCPLL_CON1_OFFSET)); buf_ptr += sprintf(buf_ptr, "MSDCPLL_CON2@0x%p = 0x%x\n", apmixed_base + MSDCPLL_CON2_OFFSET, MSDC_READ32(apmixed_base + MSDCPLL_CON2_OFFSET)); buf_ptr += sprintf(buf_ptr, "MSDCPLL_PWR_CON0@0x%p = 0x%x, bit[0] shall 1b\n", apmixed_base + MSDCPLL_PWR_CON0_OFFSET, MSDC_READ32(apmixed_base + MSDCPLL_PWR_CON0_OFFSET)); *buf_ptr = '\0'; SPREAD_PRINTF(buff, size, m, "%s", buffer); } } void msdc_dump_clock_sts(char **buff, unsigned long *size, struct seq_file *m, struct msdc_host *host) { msdc_dump_clock_sts_core(buff, size, m, host); } void msdc_clk_enable_and_stable(struct msdc_host *host) { void __iomem *base = host->base; u32 div, mode, hs400_div_dis; u32 val; msdc_clk_enable(host); val = MSDC_READ32(MSDC_CFG); GET_FIELD(val, CFG_CKDIV_SHIFT, CFG_CKDIV_MASK, div); GET_FIELD(val, CFG_CKMOD_SHIFT, CFG_CKMOD_MASK, mode); GET_FIELD(val, CFG_CKMOD_HS400_SHIFT, CFG_CKMOD_HS400_MASK, hs400_div_dis); msdc_clk_stable(host, mode, div, hs400_div_dis); } #endif /*if !defined(FPGA_PLATFORM)*/ /**************************************************************/ /* Section 4: GPIO and Pad */ /**************************************************************/ #if !defined(FPGA_PLATFORM) /* * Power off card on the 2 bad card conditions: * 1. if dat pins keep high when pulled low or * 2. dat pins alway keeps high */ int msdc_io_check(struct msdc_host *host) { #if 0 int i; void __iomem *base = host->base; unsigned long polling_tmo = 0; #ifndef SD_GPIO_PAD_A_EN void __iomem *pupd_addr[3] = { MSDC1_PUPD_DAT0_ADDR, MSDC1_PUPD_DAT1_ADDR, MSDC1_PUPD_DAT2_ADDR }; void __iomem *r0_addr[3] = { MSDC1_R0_DAT0_ADDR, MSDC1_R0_DAT0_ADDR, MSDC1_R0_DAT0_ADDR }; void __iomem *r1_addr[3] = { MSDC1_R1_DAT0_ADDR, MSDC1_R1_DAT0_ADDR, MSDC1_R1_DAT0_ADDR }; u32 pupd_mask[3] = { MSDC1_PUPD_DAT0_MASK, MSDC1_PUPD_DAT1_MASK, MSDC1_PUPD_DAT2_MASK }; u32 r0_mask[3] = { MSDC1_R0_DAT0_MASK, MSDC1_R0_DAT1_MASK, MSDC1_R0_DAT2_MASK }; u32 r1_mask[3] = { MSDC1_R1_DAT0_MASK, MSDC1_R1_DAT1_MASK, MSDC1_R1_DAT2_MASK }; #else void __iomem *pupd_addr[3] = { MSDC1_PUPD_DAT0_ADDR_A, MSDC1_PUPD_DAT1_ADDR_A, MSDC1_PUPD_DAT2_ADDR_A }; void __iomem *r0_addr[3] = { MSDC1_R0_DAT0_ADDR_A, MSDC1_R0_DAT0_ADDR_A, MSDC1_R0_DAT0_ADDR_A }; void __iomem *r1_addr[3] = { MSDC1_R1_DAT0_ADDR_A, MSDC1_R1_DAT0_ADDR_A, MSDC1_R1_DAT0_ADDR_A }; u32 pupd_mask[3] = { MSDC1_PUPD_DAT0_MASK_A, MSDC1_PUPD_DAT1_MASK_A, MSDC1_PUPD_DAT2_MASK_A }; u32 r0_mask[3] = { MSDC1_R0_DAT0_MASK_A, MSDC1_R0_DAT1_MASK_A, MSDC1_R0_DAT2_MASK_A }; u32 r1_mask[3] = { MSDC1_R1_DAT0_MASK_A, MSDC1_R1_DAT1_MASK_A, MSDC1_R1_DAT2_MASK_A }; #endif u32 check_patterns[3] = {0xE0000, 0xD0000, 0xB0000}; u32 orig_pull; u32 orig_r0; u32 orig_r1; if (host->id != 1) return 0; if (host->block_bad_card) goto SET_BAD_CARD; for (i = 0; i < 3; i++) { MSDC_GET_FIELD(pupd_addr[i], pupd_mask[i], orig_pull); MSDC_GET_FIELD(r0_addr[i], r0_mask[i], orig_r0); MSDC_GET_FIELD(r1_addr[i], r1_mask[i], orig_r1); /*R1R0=00:High-Z, R1R0=01:10kohm,R1R0=10:50kohm,R1R0=11:8kohm*/ MSDC_SET_FIELD(pupd_addr[i], pupd_mask[i], MSDC1_PD); MSDC_SET_FIELD(r0_addr[i], r0_mask[i], 0x1); MSDC_SET_FIELD(r1_addr[i], r1_mask[i], 0x1); polling_tmo = jiffies + POLLING_PINS; while ((MSDC_READ32(MSDC_PS) & 0xF0000) != check_patterns[i]) { if (time_after(jiffies, polling_tmo)) { /* Exception handling for * some good card with * pull up strength greater * than pull up strength * of gpio. */ if ((MSDC_READ32(MSDC_PS) & 0xF0000) == 0xF0000) break; pr_notice("msdc%d DAT%d pin get wrong, ps = 0x%x!\n", host->id, i, MSDC_READ32(MSDC_PS)); goto SET_BAD_CARD; } } MSDC_SET_FIELD(pupd_addr[i], pupd_mask[i], orig_pull); MSDC_SET_FIELD(r0_addr[i], r0_mask[i], orig_r0); MSDC_SET_FIELD(r1_addr[i], r1_mask[i], orig_r1); } return 0; SET_BAD_CARD: msdc_set_bad_card_and_remove(host); return 1; #endif return 0; } void msdc_dump_padctl_by_id(char **buff, unsigned long *size, struct seq_file *m, u32 id) { if (!gpio_base || !msdc_io_cfg_bases[id]) { SPREAD_PRINTF(buff, size, m, "err: gpio_base=%p, msdc_io_cfg_bases[%d]=%p\n", gpio_base, id, msdc_io_cfg_bases[id]); return; } if (id == 0) { SPREAD_PRINTF(buff, size, m, "MSDC0 MODE22 [0x%p] =0x%8x\tshould: 0x1???????\n", MSDC0_GPIO_MODE22, MSDC_READ32(MSDC0_GPIO_MODE22)); SPREAD_PRINTF(buff, size, m, "MSDC0 MODE23 [0x%p] =0x%8x\tshould: 0x11111111\n", MSDC0_GPIO_MODE23, MSDC_READ32(MSDC0_GPIO_MODE23)); SPREAD_PRINTF(buff, size, m, "MSDC0 MODE24 [0x%p] =0x%8x\tshould: 0x?????111\n", MSDC0_GPIO_MODE24, MSDC_READ32(MSDC0_GPIO_MODE24)); SPREAD_PRINTF(buff, size, m, "MSDC0 IES [0x%p] =0x%8x\tshould: 0x3FFC????\n", MSDC0_GPIO_IES, MSDC_READ32(MSDC0_GPIO_IES)); SPREAD_PRINTF(buff, size, m, "MSDC0 SMT [0x%p] =0x%8x\tshould: 0x??????7C\n", MSDC0_GPIO_SMT, MSDC_READ32(MSDC0_GPIO_SMT)); SPREAD_PRINTF(buff, size, m, "MSDC0 TDSEL0 [0x%p] =0x%8x\n", MSDC0_GPIO_TDSEL0, MSDC_READ32(MSDC0_GPIO_TDSEL0)); SPREAD_PRINTF(buff, size, m, "MSDC0 RDSEL0 [0x%p] =0x%8x, [0x%p] = 0x%8x\n", MSDC0_GPIO_RDSEL0, MSDC_READ32(MSDC0_GPIO_RDSEL0), MSDC0_GPIO_RDSEL0_1, MSDC_READ32(MSDC0_GPIO_RDSEL0_1)); SPREAD_PRINTF(buff, size, m, "MSDC0 DRV0 [0x%p] =0x%8x\n", MSDC0_GPIO_DRV0, MSDC_READ32(MSDC0_GPIO_DRV0)); SPREAD_PRINTF(buff, size, m, "PUPD/R1/R0: dat/cmd:0/0/1, clk/dst: 1/1/0\n"); SPREAD_PRINTF(buff, size, m, "MSDC0 PUPD0 [0x%p] =0x%8x\tshould: 0x?????401\n", MSDC0_GPIO_PUPD0, MSDC_READ32(MSDC0_GPIO_PUPD0)); SPREAD_PRINTF(buff, size, m, "MSDC0 R0 [0x%p] =0x%8x\tshould: 0x?????BFE\n", MSDC0_GPIO_R0, MSDC_READ32(MSDC0_GPIO_R0)); SPREAD_PRINTF(buff, size, m, "MSDC0 R1 [0x%p] =0x%8x\tshould: 0x?????401\n", MSDC0_GPIO_R1, MSDC_READ32(MSDC0_GPIO_R1)); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN SPREAD_PRINTF(buff, size, m, "MSDC1_A_GPIO_MISC [0x%p] =0x%8x\tshould: bit[9]=0\n", MSDC1_GPIO_MISC, MSDC_READ32(MSDC1_GPIO_MISC)); SPREAD_PRINTF(buff, size, m, "MSDC1 MODE6 [0x%p] =0x%8x\tshould: 0x11111???\n", MSDC1_GPIO_MODE6, MSDC_READ32(MSDC1_GPIO_MODE6)); SPREAD_PRINTF(buff, size, m, "MSDC1 MODE7 [0x%p] =0x%8x\tshould: 0x???????1\n", MSDC1_GPIO_MODE7, MSDC_READ32(MSDC1_GPIO_MODE7)); SPREAD_PRINTF(buff, size, m, "MSDC1 IES [0x%p] =0x%8x\t 21-16bits should: 2b111111\n", MSDC1_GPIO_IES, MSDC_READ32(MSDC1_GPIO_IES)); SPREAD_PRINTF(buff, size, m, "MSDC1 SMT [0x%p] =0x%8x\t 8-6bits should: 2b111\n", MSDC1_GPIO_SMT, MSDC_READ32(MSDC1_GPIO_SMT)); SPREAD_PRINTF(buff, size, m, "MSDC1 TDSEL0 [0x%p] =0x%8x, [0x%p] =0x%8x\n", MSDC1_GPIO_TDSEL0, MSDC_READ32(MSDC1_GPIO_TDSEL0), MSDC1_GPIO_TDSEL0_1, MSDC_READ32(MSDC1_GPIO_TDSEL0_1)); SPREAD_PRINTF(buff, size, m, "should 1.8v: sleep: TBD, awake: TBD\n"); SPREAD_PRINTF(buff, size, m, "MSDC1 RDSEL0 [0x%p] =0x%8x\n", MSDC1_GPIO_RDSEL0, MSDC_READ32(MSDC1_GPIO_RDSEL0)); SPREAD_PRINTF(buff, size, m, "1.8V: TBD, 2.9v: TBD\n"); SPREAD_PRINTF(buff, size, m, "MSDC1 DRV0 [0x%p] =0x%8x\n", MSDC1_GPIO_DRV0, MSDC_READ32(MSDC1_GPIO_DRV0)); SPREAD_PRINTF(buff, size, m, "MSDC1 PUPD0 [0x%p] =0x%8x\tshould: 0x??????01\n", MSDC1_GPIO_PUPD0, MSDC_READ32(MSDC1_GPIO_PUPD0)); SPREAD_PRINTF(buff, size, m, "MSDC1 R0 [0x%p] =0x%8x\tshould: 0x??????00\n", MSDC1_GPIO_R0, MSDC_READ32(MSDC1_GPIO_R0)); SPREAD_PRINTF(buff, size, m, "MSDC1 R1 [0x%p] =0x%8x\tshould: 0x??????3F\n", MSDC1_GPIO_R1, MSDC_READ32(MSDC1_GPIO_R1)); #else SPREAD_PRINTF(buff, size, m, "MSDC1_A_GPIO_MISC [0x%p] =0x%8x\tshould: bit[9]=1\n", MSDC1_GPIO_MISC, MSDC_READ32(MSDC1_GPIO_MISC)); SPREAD_PRINTF(buff, size, m, "MSDC1_A MODE1 [0x%p] =0x%8x\tshould: 0x111111??\n", MSDC1_GPIO_MODE1, MSDC_READ32(MSDC1_GPIO_MODE1)); SPREAD_PRINTF(buff, size, m, "MSDC1_A IES [0x%p] =0x%8x\tshould: 0x????7E??\n", MSDC1_GPIO_IES_A, MSDC_READ32(MSDC1_GPIO_IES_A)); SPREAD_PRINTF(buff, size, m, "MSDC1_A SMT [0x%p] =0x%8x\tshould: 0x??????1?\n", MSDC1_GPIO_SMT_A, MSDC_READ32(MSDC1_GPIO_SMT_A)); SPREAD_PRINTF(buff, size, m, "MSDC1_A TDSEL0 [0x%p] =0x%8x\n", MSDC1_GPIO_TDSEL0_A, MSDC_READ32(MSDC1_GPIO_TDSEL0_A)); SPREAD_PRINTF(buff, size, m, "should 1.8v: sleep: TBD, awake: TBD\n"); SPREAD_PRINTF(buff, size, m, "MSDC1_A RDSEL0 [0x%p] =0x%8x\n", MSDC1_GPIO_RDSEL0_A, MSDC_READ32(MSDC1_GPIO_RDSEL0_A)); SPREAD_PRINTF(buff, size, m, "1.8V: TBD, 2.9v: TBD\n"); SPREAD_PRINTF(buff, size, m, "MSDC1_A DRV0 [0x%p] =0x%8x\n", MSDC1_GPIO_DRV0_A, MSDC_READ32(MSDC1_GPIO_DRV0_A)); SPREAD_PRINTF(buff, size, m, "MSDC1_A PUPD0 [0x%p] =0x%8x\tshould: 0x??????01\n", MSDC1_GPIO_PUPD0_A, MSDC_READ32(MSDC1_GPIO_PUPD0_A)); SPREAD_PRINTF(buff, size, m, "MSDC1_A R0 [0x%p] =0x%8x\tshould: 0x??????00\n", MSDC1_GPIO_R0_A, MSDC_READ32(MSDC1_GPIO_R0_A)); SPREAD_PRINTF(buff, size, m, "MSDC1_A R1 [0x%p] =0x%8x\tshould: 0x??????3F\n", MSDC1_GPIO_R1_A, MSDC_READ32(MSDC1_GPIO_R1_A)); #endif } } void msdc_set_pin_mode(struct msdc_host *host) { if (host->id == 0) { MSDC_SET_FIELD(MSDC0_GPIO_MODE22, 0xF0000000, 0x1); MSDC_SET_FIELD(MSDC0_GPIO_MODE23, 0xFFFFFFFF, 0x11111111); MSDC_SET_FIELD(MSDC0_GPIO_MODE24, 0x00000FFF, 0x111); } else if (host->id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_MISC, MSDC1_PIN_MUX_SEL_MASK_A, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_MODE6, 0xFFFFF000, 0x11111); MSDC_SET_FIELD(MSDC1_GPIO_MODE7, 0x0000000F, 0x1); #else MSDC_SET_FIELD(MSDC1_GPIO_MISC, MSDC1_PIN_MUX_SEL_MASK_A, 0x1); MSDC_SET_FIELD(MSDC1_GPIO_MODE1, 0xFFFFFF00, 0x111111); #endif } } void msdc_set_ies_by_id(u32 id, int set_ies) { if (id == 0) { MSDC_SET_FIELD(MSDC0_GPIO_IES, MSDC0_IES_ALL_MASK, (set_ies ? 0xFFF : 0)); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_IES, MSDC1_IES_ALL_MASK, (set_ies ? 0x3F : 0)); #else MSDC_SET_FIELD(MSDC1_GPIO_IES_A, MSDC1_IES_ALL_MASK_A, (set_ies ? 0x3F : 0)); #endif } } void msdc_set_smt_by_id(u32 id, int set_smt) { if (id == 0) { MSDC_SET_FIELD(MSDC0_GPIO_SMT, MSDC0_SMT_ALL_MASK, (set_smt ? 0xF : 0)); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_SMT, MSDC1_SMT_ALL_MASK, (set_smt ? 0x7 : 0)); #else MSDC_SET_FIELD(MSDC1_GPIO_SMT_A, MSDC1_SMT_ALL_MASK_A, (set_smt ? 0x7 : 0)); #endif } } void msdc_set_tdsel_by_id(u32 id, u32 flag, u32 value) { u32 cust_val; if (id == 0) { if (flag == MSDC_TDRDSEL_CUST) cust_val = value; else cust_val = 0; MSDC_SET_FIELD(MSDC0_GPIO_TDSEL0, MSDC0_TDSEL0_CMD_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_TDSEL0, MSDC0_TDSEL0_DAT_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_TDSEL0, MSDC0_TDSEL0_CLK_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_TDSEL0, MSDC0_TDSEL0_DSL_MASK, cust_val); } else if (id == 1) { if (flag == MSDC_TDRDSEL_CUST) cust_val = value; else cust_val = 0; #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_TDSEL0_1, MSDC1_TDSEL0_DAT_MASK, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_TDSEL0, MSDC1_TDSEL0_CMD_MASK, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_TDSEL0, MSDC1_TDSEL0_CLK_MASK, cust_val); #else MSDC_SET_FIELD(MSDC1_GPIO_TDSEL0_A, MSDC1_TDSEL0_DAT_MASK_A, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_TDSEL0_A, MSDC1_TDSEL0_CMD_MASK_A, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_TDSEL0_A, MSDC1_TDSEL0_CLK_MASK_A, cust_val); #endif } } void msdc_set_rdsel_by_id(u32 id, u32 flag, u32 value) { u32 cust_val; if (id == 0) { if (flag == MSDC_TDRDSEL_CUST) cust_val = value; else cust_val = 0; MSDC_SET_FIELD(MSDC0_GPIO_RDSEL0, MSDC0_RDSEL0_CMD_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_RDSEL0, MSDC0_RDSEL0_DAT_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_RDSEL0, MSDC0_RDSEL0_CLK_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_RDSEL0, MSDC0_RDSEL0_DSL_MASK, cust_val); MSDC_SET_FIELD(MSDC0_GPIO_RDSEL0_1, MSDC0_RDSEL0_RSTB_MASK, cust_val); } else if (id == 1) { if (flag == MSDC_TDRDSEL_CUST) cust_val = value; else cust_val = 0; #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_RDSEL0, MSDC1_RDSEL0_CMD_MASK, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_RDSEL0, MSDC1_RDSEL0_DAT_MASK, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_RDSEL0, MSDC1_RDSEL0_CLK_MASK, cust_val); #else MSDC_SET_FIELD(MSDC1_GPIO_RDSEL0_A, MSDC1_RDSEL0_CMD_MASK_A, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_RDSEL0_A, MSDC1_RDSEL0_DAT_MASK_A, cust_val); MSDC_SET_FIELD(MSDC1_GPIO_RDSEL0_A, MSDC1_RDSEL0_CLK_MASK_A, cust_val); #endif } } void msdc_get_tdsel_by_id(u32 id, u32 *value) { if (id == 0) { MSDC_GET_FIELD(MSDC0_GPIO_TDSEL0, MSDC0_TDSEL0_CMD_MASK, *value); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_GET_FIELD(MSDC1_GPIO_TDSEL0, MSDC1_TDSEL0_CMD_MASK, *value); #else MSDC_GET_FIELD(MSDC1_GPIO_TDSEL0_A, MSDC1_TDSEL0_CMD_MASK_A, *value); #endif } } void msdc_get_rdsel_by_id(u32 id, u32 *value) { if (id == 0) { MSDC_GET_FIELD(MSDC0_GPIO_RDSEL0, MSDC0_RDSEL0_CMD_MASK, *value); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_GET_FIELD(MSDC1_GPIO_RDSEL0, MSDC1_RDSEL0_CMD_MASK, *value); #else MSDC_GET_FIELD(MSDC1_GPIO_RDSEL0_A, MSDC1_RDSEL0_CMD_MASK_A, *value); #endif } } void msdc_set_sr_by_id(u32 id, int clk, int cmd, int dat, int rst, int ds) { return; } void msdc_set_driving_by_id(u32 id, struct msdc_hw_driving *driving) { if (id == 0) { MSDC_SET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_DSL_MASK, driving->ds_drv); MSDC_SET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_DAT_MASK, driving->dat_drv); MSDC_SET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_CMD_MASK, driving->cmd_drv); MSDC_SET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_CLK_MASK, driving->clk_drv); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_DRV0, MSDC1_DRV0_CMD_MASK, driving->cmd_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0, MSDC1_DRV0_CLK_MASK, driving->clk_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0, MSDC1_DRV0_DAT_MASK, driving->dat_drv); #else MSDC_SET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_CMD_MASK_A, driving->cmd_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_CLK_MASK_A, driving->clk_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_DAT0_MASK_A, driving->dat_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_DAT1_MASK_A, driving->dat_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_DAT2_MASK_A, driving->dat_drv); MSDC_SET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_DAT3_MASK_A, driving->dat_drv); #endif } } void msdc_get_driving_by_id(u32 id, struct msdc_hw_driving *driving) { if (id == 0) { MSDC_GET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_DSL_MASK, driving->ds_drv); MSDC_GET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_DAT_MASK, driving->rst_drv); MSDC_GET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_CMD_MASK, driving->cmd_drv); MSDC_GET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_CLK_MASK, driving->clk_drv); MSDC_GET_FIELD(MSDC0_GPIO_DRV0, MSDC0_DRV0_DAT_MASK, driving->dat_drv); } else if (id == 1) { #ifndef SD_GPIO_PAD_A_EN MSDC_GET_FIELD(MSDC1_GPIO_DRV0, MSDC1_DRV0_CMD_MASK, driving->cmd_drv); MSDC_GET_FIELD(MSDC1_GPIO_DRV0, MSDC1_DRV0_CLK_MASK, driving->clk_drv); MSDC_GET_FIELD(MSDC1_GPIO_DRV0, MSDC1_DRV0_DAT_MASK, driving->dat_drv); #else MSDC_GET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_CMD_MASK_A, driving->cmd_drv); MSDC_GET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_CLK_MASK_A, driving->clk_drv); MSDC_GET_FIELD(MSDC1_GPIO_DRV0_A, MSDC1_DRV0_DAT0_MASK_A, driving->dat_drv); #endif } } /* msdc pin config * MSDC0 * PUPD/R1/R0 * 0/0/0: High-Z * 0/1/0: Pull-up with 50Kohm * 0/0/1: Pull-up with 10Kohm * 0/1/1: Pull-up with 50Kohm//10Kohm * 1/0/0: High-Z * 1/1/0: Pull-down with 50Kohm * 1/0/1: Pull-down with 10Kohm * 1/1/1: Pull-down with 50Kohm//10Kohm */ void msdc_pin_config_by_id(u32 id, u32 mode) { if (id == 0) { /* 1. don't pull CLK high; * 2. Don't toggle RST to prevent from entering boot mode */ if (mode == MSDC_PIN_PULL_NONE) { /* Switch MSDC0_* to no ohm PU */ MSDC_SET_FIELD(MSDC0_GPIO_PUPD0, MSDC0_PUPD_ALL_MASK, 0x0); MSDC_SET_FIELD(MSDC0_GPIO_R0, MSDC0_R0_ALL_MASK, 0x0); MSDC_SET_FIELD(MSDC0_GPIO_R1, MSDC0_R1_ALL_MASK, 0x0); } else if (mode == MSDC_PIN_PULL_DOWN) { /* Switch MSDC0_* to 50K ohm PD */ MSDC_SET_FIELD(MSDC0_GPIO_PUPD0, MSDC0_PUPD_ALL_MASK, 0x7FF); MSDC_SET_FIELD(MSDC0_GPIO_R0, MSDC0_R0_ALL_MASK, 0); MSDC_SET_FIELD(MSDC0_GPIO_R1, MSDC0_R1_ALL_MASK, 0xFFF); } else if (mode == MSDC_PIN_PULL_UP) { /* Switch MSDC0_CLK to 50K ohm PD, * MSDC0_CMD/MSDC0_DAT* to 10K ohm PU, * MSDC0_DSL to 50K ohm PD */ MSDC_SET_FIELD(MSDC0_GPIO_PUPD0, MSDC0_PUPD_ALL_MASK, 0x401); MSDC_SET_FIELD(MSDC0_GPIO_R0, MSDC0_R0_ALL_MASK, 0xBFE); MSDC_SET_FIELD(MSDC0_GPIO_R1, MSDC0_R1_ALL_MASK, 0x401); } } else if (id == 1) { if (mode == MSDC_PIN_PULL_NONE) { /* Switch MSDC1_* to no ohm PU */ #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_PUPD0, MSDC1_PUPD_ALL_MASK, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R0, MSDC1_R0_ALL_MASK, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R1, MSDC1_R1_ALL_MASK, 0x0); #else MSDC_SET_FIELD(MSDC1_GPIO_PUPD0_A, MSDC1_PUPD_ALL_MASK_A, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R0_A, MSDC1_R0_ALL_MASK_A, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R1_A, MSDC1_R1_ALL_MASK_A, 0x0); #endif } else if (mode == MSDC_PIN_PULL_DOWN) { /* Switch MSDC1_* to 50K ohm PD */ #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_PUPD0, MSDC1_PUPD_ALL_MASK, 0x3F); MSDC_SET_FIELD(MSDC1_GPIO_R0, MSDC1_R0_ALL_MASK, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R1, MSDC1_R1_ALL_MASK, 0x3F); #else MSDC_SET_FIELD(MSDC1_GPIO_PUPD0_A, MSDC1_PUPD_ALL_MASK_A, 0x3F); MSDC_SET_FIELD(MSDC1_GPIO_R0_A, MSDC1_R0_ALL_MASK_A, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R1_A, MSDC1_R1_ALL_MASK_A, 0x3F); #endif } else if (mode == MSDC_PIN_PULL_UP) { /* Switch MSDC1_CLK to 50K ohm PD, * MSDC1_CMD/MSDC1_DAT* to 50K ohm PU */ #ifndef SD_GPIO_PAD_A_EN MSDC_SET_FIELD(MSDC1_GPIO_PUPD0, MSDC1_PUPD_ALL_MASK, 0x1); MSDC_SET_FIELD(MSDC1_GPIO_R0, MSDC1_R0_ALL_MASK, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R1, MSDC1_R1_ALL_MASK, 0x3F); #else MSDC_SET_FIELD(MSDC1_GPIO_PUPD0_A, MSDC1_PUPD_ALL_MASK_A, 0x1); MSDC_SET_FIELD(MSDC1_GPIO_R0_A, MSDC1_R0_ALL_MASK_A, 0x0); MSDC_SET_FIELD(MSDC1_GPIO_R1_A, MSDC1_R1_ALL_MASK_A, 0x3F); #endif } } } #endif /*if !defined(FPGA_PLATFORM)*/ /**************************************************************/ /* Section 5: Device Tree Init function */ /* This function is placed here so that all */ /* functions and variables used by it has already */ /* been declared */ /**************************************************************/ /* * parse pinctl settings * Driver strength */ #if !defined(FPGA_PLATFORM) static int msdc_get_pinctl_settings(struct msdc_host *host, struct device_node *np) { struct device_node *pinctl_node, *pins_node; static char const * const pinctl_names[] = { "pinctl", "pinctl_hs400", "pinctl_hs200", "pinctl_sdr104", "pinctl_sdr50", "pinctl_ddr50" }; /* sequence shall be the same as sequence in msdc_hw_driving */ static char const * const pins_names[] = { "pins_cmd", "pins_dat", "pins_clk", "pins_rst", "pins_ds" }; unsigned char *pin_drv; int i, j; host->hw->driving_applied = &host->hw->driving; for (i = 0; i < ARRAY_SIZE(pinctl_names); i++) { pinctl_node = of_parse_phandle(np, pinctl_names[i], 0); if (strcmp(pinctl_names[i], "pinctl") == 0) pin_drv = (unsigned char *)&host->hw->driving; else if (strcmp(pinctl_names[i], "pinctl_hs400") == 0) pin_drv = (unsigned char *)&host->hw->driving_hs400; else if (strcmp(pinctl_names[i], "pinctl_hs200") == 0) pin_drv = (unsigned char *)&host->hw->driving_hs200; else if (strcmp(pinctl_names[i], "pinctl_sdr104") == 0) pin_drv = (unsigned char *)&host->hw->driving_sdr104; else if (strcmp(pinctl_names[i], "pinctl_sdr50") == 0) pin_drv = (unsigned char *)&host->hw->driving_sdr50; else if (strcmp(pinctl_names[i], "pinctl_ddr50") == 0) pin_drv = (unsigned char *)&host->hw->driving_ddr50; else continue; for (j = 0; j < ARRAY_SIZE(pins_names); j++) { pins_node = of_get_child_by_name(pinctl_node, pins_names[j]); if (pins_node) of_property_read_u8(pins_node, "drive-strength", pin_drv); pin_drv++; } } return 0; } #endif /* Get msdc register settings * 1. internal data delay for tuning, FIXME: can be removed when use data tune? * 2. sample edge */ static int msdc_get_register_settings(struct msdc_host *host, struct device_node *np) { struct device_node *register_setting_node = NULL; /* parse hw property settings */ register_setting_node = of_parse_phandle(np, "register_setting", 0); if (register_setting_node) { of_property_read_u8(register_setting_node, "cmd_edge", &host->hw->cmd_edge); of_property_read_u8(register_setting_node, "rdata_edge", &host->hw->rdata_edge); of_property_read_u8(register_setting_node, "wdata_edge", &host->hw->wdata_edge); } else { pr_notice("[msdc%d] register_setting is not found in DT\n", host->id); } return 0; } /* * msdc_of_parse() - parse host's device-tree node * @host: host whose node should be parsed. * */ int msdc_of_parse(struct platform_device *pdev, struct mmc_host *mmc) { struct device_node *np; struct msdc_host *host = mmc_priv(mmc); int ret = 0; int len = 0; u8 id = 0; const char *dup_name; /*use to solve UAF issue :ALPS04094268*/ int boot_type; np = mmc->parent->of_node; /* mmcx node in project dts */ if (of_property_read_u8(np, "index", &id)) { pr_notice("[%s] host index not specified in device tree\n", pdev->dev.of_node->name); return -1; } /* Add get_boot_type check and return ENODEV if not eMMC boot */ boot_type = get_boot_type(); if ((boot_type != BOOTDEV_SDMMC) && (id == 0)) return -ENODEV; host->id = id; pdev->id = id; pr_notice("DT probe %s%d!\n", pdev->dev.of_node->name, id); ret = mmc_of_parse(mmc); if (ret) { pr_notice("%s: mmc of parse error!!: %d\n", __func__, ret); return ret; } host->mmc = mmc; host->hw = kzalloc(sizeof(struct msdc_hw), GFP_KERNEL); /* iomap register */ host->base = of_iomap(np, 0); if (!host->base) { pr_notice("[msdc%d] of_iomap failed\n", mmc->index); return -ENOMEM; } /* get irq # */ host->irq = irq_of_parse_and_map(np, 0); pr_notice("[msdc%d] get irq # %d\n", host->id, host->irq); WARN_ON(host->irq < 0); #if !defined(FPGA_PLATFORM) /* get clk_src */ if (of_property_read_u8(np, "clk_src", &host->hw->clk_src)) { pr_notice("[msdc%d] error: clk_src isn't found in device tree.\n", host->id); WARN_ON(1); } host->hclk = msdc_get_hclk(host->id, host->hw->clk_src); #endif if (of_find_property(np, "sd-uhs-ddr208", &len)) host->hw->flags |= MSDC_SDIO_DDR208; /* Returns 0 on success, -EINVAL if the property does not exist, * -ENODATA if property does not have a value, and -EOVERFLOW if the * property data isn't large enough. */ if (of_property_read_u8(np, "host_function", &host->hw->host_function)) pr_notice("[msdc%d] host_function isn't found in device tree\n", host->id); /* get cd_gpio and cd_level */ ret = of_get_named_gpio(np, "cd-gpios", 0); if (ret >= 0) { cd_gpio = ret; if (of_property_read_u8(np, "cd_level", &host->hw->cd_level)) pr_info("[msdc%d] cd_level isn't found in device tree\n", host->id); } msdc_get_register_settings(host, np); #if !defined(FPGA_PLATFORM) msdc_get_pinctl_settings(host, np); mmc->supply.vmmc = regulator_get(mmc_dev(mmc), "vmmc"); if (IS_ERR(mmc->supply.vmmc)) { pr_info("err=%ld,failed to get vmmc\n", PTR_ERR(mmc->supply.vmmc)); goto vmmc_fail; } mmc->supply.vqmmc = regulator_get(mmc_dev(mmc), "vqmmc"); if (IS_ERR(mmc->supply.vqmmc)) { pr_info("err=%ld ,failed to get vqmmc\n", PTR_ERR(mmc->supply.vqmmc)); goto vqmmc_fail; } #else msdc_fpga_pwr_init(); #endif #if !defined(FPGA_PLATFORM) if (host->hw->host_function == MSDC_EMMC) { np = of_find_compatible_node(NULL, NULL, "mediatek,msdc0_top"); host->base_top = of_iomap(np, 0); } else if (host->hw->host_function == MSDC_SD) { np = of_find_compatible_node(NULL, NULL, "mediatek,msdc1_top"); host->base_top = of_iomap(np, 0); } if (host->base_top) pr_debug("of_iomap for MSDC%d TOP base @ 0x%p\n", host->id, host->base_top); #endif #if defined(CFG_DEV_MSDC3) if (host->hw->host_function == MSDC_SDIO) { host->hw->flags |= MSDC_EXT_SDIO_IRQ; host->hw->request_sdio_eirq = mt_sdio_ops[3].sdio_request_eirq; host->hw->enable_sdio_eirq = mt_sdio_ops[3].sdio_enable_eirq; host->hw->disable_sdio_eirq = mt_sdio_ops[3].sdio_disable_eirq; host->hw->register_pm = mt_sdio_ops[3].sdio_register_pm; } #endif /* fix uaf(use afer free) issue:backup pdev->name, * device_rename will free pdev->name */ pdev->name = kstrdup(pdev->name, GFP_KERNEL); /* device rename */ if ((host->id == 0) && !device_rename(mmc->parent, "bootdevice")) pr_notice("[msdc%d] device renamed to bootdevice.\n", host->id); else if ((host->id == 1) && !device_rename(mmc->parent, "externdevice")) pr_notice("[msdc%d] device renamed to externdevice.\n", host->id); else if ((host->id == 0) || (host->id == 1)) pr_notice("[msdc%d] error: device renamed failed.\n", host->id); dup_name = pdev->name; pdev->name = pdev->dev.kobj.name; kfree_const(dup_name); #if !defined(FPGA_PLATFORM) && !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) if (host->id == 1) { sd_oc.nb.notifier_call = msdc_sd_event; INIT_WORK(&sd_oc.work, sdcard_oc_handler); } #endif return host->id; vqmmc_fail: regulator_put(mmc->supply.vmmc); vmmc_fail: kfree(host->hw); return -1; } int msdc_dt_init(struct platform_device *pdev, struct mmc_host *mmc) { int id; #ifndef FPGA_PLATFORM #ifndef SD_GPIO_PAD_A_EN static char const * const ioconfig_names[] = { MSDC0_IOCFG_NAME, MSDC1_IOCFG_NAME }; #else static char const * const ioconfig_names[] = { MSDC0_IOCFG_NAME, MSDC1_A_IOCFG_NAME }; #endif struct device_node *np; #endif id = msdc_of_parse(pdev, mmc); if (id < 0) { pr_notice("%s: msdc_of_parse error!!: %d\n", __func__, id); return id; } #ifndef FPGA_PLATFORM if (gpio_base == NULL) { np = of_find_compatible_node(NULL, NULL, "mediatek,gpio"); gpio_base = of_iomap(np, 0); pr_debug("of_iomap for gpio base @ 0x%p\n", gpio_base); } if (msdc_io_cfg_bases[id] == NULL) { np = of_find_compatible_node(NULL, NULL, ioconfig_names[id]); msdc_io_cfg_bases[id] = of_iomap(np, 0); pr_debug("of_iomap for MSDC%d IOCFG base @ 0x%p\n", id, msdc_io_cfg_bases[id]); } if (apmixed_base == NULL) { np = of_find_compatible_node(NULL, NULL, "mediatek,apmixed"); apmixed_base = of_iomap(np, 0); pr_debug("of_iomap for apmixed base @ 0x%p\n", apmixed_base); } if (topckgen_base == NULL) { np = of_find_compatible_node(NULL, NULL, "mediatek,topckgen"); topckgen_base = of_iomap(np, 0); pr_debug("of_iomap for topckgen base @ 0x%p\n", topckgen_base); } if (infracfg_ao_base == NULL) { np = of_find_compatible_node(NULL, NULL, "mediatek,infracfg_ao"); infracfg_ao_base = of_iomap(np, 0); pr_debug("of_iomap for infracfg_ao base @ 0x%p\n", infracfg_ao_base); } #endif return 0; } /**************************************************************/ /* Section 7: For msdc register dump */ /**************************************************************/ u16 msdc_offsets[] = { OFFSET_MSDC_CFG, OFFSET_MSDC_IOCON, OFFSET_MSDC_PS, OFFSET_MSDC_INT, OFFSET_MSDC_INTEN, OFFSET_MSDC_FIFOCS, OFFSET_SDC_CFG, OFFSET_SDC_CMD, OFFSET_SDC_ARG, OFFSET_SDC_STS, OFFSET_SDC_RESP0, OFFSET_SDC_RESP1, OFFSET_SDC_RESP2, OFFSET_SDC_RESP3, OFFSET_SDC_BLK_NUM, OFFSET_SDC_VOL_CHG, OFFSET_SDC_CSTS, OFFSET_SDC_CSTS_EN, OFFSET_SDC_DCRC_STS, OFFSET_SDC_ADV_CFG0, OFFSET_EMMC_CFG0, OFFSET_EMMC_CFG1, OFFSET_EMMC_STS, OFFSET_EMMC_IOCON, OFFSET_SDC_ACMD_RESP, OFFSET_MSDC_DMA_SA_HIGH, OFFSET_MSDC_DMA_SA, OFFSET_MSDC_DMA_CA, OFFSET_MSDC_DMA_CTRL, OFFSET_MSDC_DMA_CFG, OFFSET_MSDC_DMA_LEN, OFFSET_MSDC_DBG_SEL, OFFSET_MSDC_DBG_OUT, OFFSET_MSDC_PATCH_BIT0, OFFSET_MSDC_PATCH_BIT1, OFFSET_MSDC_PATCH_BIT2, OFFSET_MSDC_PAD_TUNE0, OFFSET_MSDC_PAD_TUNE1, OFFSET_MSDC_HW_DBG, OFFSET_MSDC_VERSION, OFFSET_EMMC50_PAD_DS_TUNE, OFFSET_EMMC50_PAD_CMD_TUNE, OFFSET_EMMC50_PAD_DAT01_TUNE, OFFSET_EMMC50_PAD_DAT23_TUNE, OFFSET_EMMC50_PAD_DAT45_TUNE, OFFSET_EMMC50_PAD_DAT67_TUNE, OFFSET_EMMC51_CFG0, OFFSET_EMMC50_CFG0, OFFSET_EMMC50_CFG1, OFFSET_EMMC50_CFG2, OFFSET_EMMC50_CFG3, OFFSET_EMMC50_CFG4, OFFSET_SDC_FIFO_CFG, OFFSET_MSDC_AES_SEL, #ifdef CONFIG_MTK_HW_FDE OFFSET_EMMC52_AES_EN, OFFSET_EMMC52_AES_CFG_GP0, OFFSET_EMMC52_AES_IV0_GP0, OFFSET_EMMC52_AES_IV1_GP0, OFFSET_EMMC52_AES_IV2_GP0, OFFSET_EMMC52_AES_IV3_GP0, OFFSET_EMMC52_AES_CTR0_GP0, OFFSET_EMMC52_AES_CTR1_GP0, OFFSET_EMMC52_AES_CTR2_GP0, OFFSET_EMMC52_AES_CTR3_GP0, OFFSET_EMMC52_AES_KEY0_GP0, OFFSET_EMMC52_AES_KEY1_GP0, OFFSET_EMMC52_AES_KEY2_GP0, OFFSET_EMMC52_AES_KEY3_GP0, OFFSET_EMMC52_AES_KEY4_GP0, OFFSET_EMMC52_AES_KEY5_GP0, OFFSET_EMMC52_AES_KEY6_GP0, OFFSET_EMMC52_AES_KEY7_GP0, OFFSET_EMMC52_AES_TKEY0_GP0, OFFSET_EMMC52_AES_TKEY1_GP0, OFFSET_EMMC52_AES_TKEY2_GP0, OFFSET_EMMC52_AES_TKEY3_GP0, OFFSET_EMMC52_AES_TKEY4_GP0, OFFSET_EMMC52_AES_TKEY5_GP0, OFFSET_EMMC52_AES_TKEY6_GP0, OFFSET_EMMC52_AES_TKEY7_GP0, OFFSET_EMMC52_AES_SWST, OFFSET_EMMC52_AES_CFG_GP1, OFFSET_EMMC52_AES_IV0_GP1, OFFSET_EMMC52_AES_IV1_GP1, OFFSET_EMMC52_AES_IV2_GP1, OFFSET_EMMC52_AES_IV3_GP1, OFFSET_EMMC52_AES_CTR0_GP1, OFFSET_EMMC52_AES_CTR1_GP1, OFFSET_EMMC52_AES_CTR2_GP1, OFFSET_EMMC52_AES_CTR3_GP1, OFFSET_EMMC52_AES_KEY0_GP1, OFFSET_EMMC52_AES_KEY1_GP1, OFFSET_EMMC52_AES_KEY2_GP1, OFFSET_EMMC52_AES_KEY3_GP1, OFFSET_EMMC52_AES_KEY4_GP1, OFFSET_EMMC52_AES_KEY5_GP1, OFFSET_EMMC52_AES_KEY6_GP1, OFFSET_EMMC52_AES_KEY7_GP1, OFFSET_EMMC52_AES_TKEY0_GP1, OFFSET_EMMC52_AES_TKEY1_GP1, OFFSET_EMMC52_AES_TKEY2_GP1, OFFSET_EMMC52_AES_TKEY3_GP1, OFFSET_EMMC52_AES_TKEY4_GP1, OFFSET_EMMC52_AES_TKEY5_GP1, OFFSET_EMMC52_AES_TKEY6_GP1, OFFSET_EMMC52_AES_TKEY7_GP1, #endif 0xFFFF /*as mark of end */ }; u16 msdc_offsets_top[] = { OFFSET_EMMC_TOP_CONTROL, OFFSET_EMMC_TOP_CMD, OFFSET_TOP_EMMC50_PAD_CTL0, OFFSET_TOP_EMMC50_PAD_DS_TUNE, OFFSET_TOP_EMMC50_PAD_DAT0_TUNE, OFFSET_TOP_EMMC50_PAD_DAT1_TUNE, OFFSET_TOP_EMMC50_PAD_DAT2_TUNE, OFFSET_TOP_EMMC50_PAD_DAT3_TUNE, OFFSET_TOP_EMMC50_PAD_DAT4_TUNE, OFFSET_TOP_EMMC50_PAD_DAT5_TUNE, OFFSET_TOP_EMMC50_PAD_DAT6_TUNE, OFFSET_TOP_EMMC50_PAD_DAT7_TUNE, 0xFFFF /*as mark of end */ };