19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 201acf691SAdam Lee /* 301acf691SAdam Lee * Copyright (C) 2013 BayHub Technology Ltd. 401acf691SAdam Lee * 501acf691SAdam Lee * Authors: Peter Guo <peter.guo@bayhubtech.com> 601acf691SAdam Lee * Adam Lee <adam.lee@canonical.com> 757322d54Sernest.zhang * Ernest Zhang <ernest.zhang@bayhubtech.com> 801acf691SAdam Lee */ 901acf691SAdam Lee 1001acf691SAdam Lee #include <linux/pci.h> 110086fc21Sernest.zhang #include <linux/mmc/host.h> 120086fc21Sernest.zhang #include <linux/mmc/mmc.h> 130086fc21Sernest.zhang #include <linux/delay.h> 147d440617SShirley Her (SC) #include <linux/iopoll.h> 154be33cf1SFred Ai #include <linux/bitfield.h> 1601acf691SAdam Lee 1701acf691SAdam Lee #include "sdhci.h" 1801acf691SAdam Lee #include "sdhci-pci.h" 19361eeda0SAdrian Hunter 20361eeda0SAdrian Hunter /* 21361eeda0SAdrian Hunter * O2Micro device registers 22361eeda0SAdrian Hunter */ 23361eeda0SAdrian Hunter 24361eeda0SAdrian Hunter #define O2_SD_MISC_REG5 0x64 25361eeda0SAdrian Hunter #define O2_SD_LD0_CTRL 0x68 26361eeda0SAdrian Hunter #define O2_SD_DEV_CTRL 0x88 27361eeda0SAdrian Hunter #define O2_SD_LOCK_WP 0xD3 28361eeda0SAdrian Hunter #define O2_SD_TEST_REG 0xD4 29361eeda0SAdrian Hunter #define O2_SD_FUNC_REG0 0xDC 30361eeda0SAdrian Hunter #define O2_SD_MULTI_VCC3V 0xEE 31361eeda0SAdrian Hunter #define O2_SD_CLKREQ 0xEC 32361eeda0SAdrian Hunter #define O2_SD_CAPS 0xE0 33361eeda0SAdrian Hunter #define O2_SD_ADMA1 0xE2 34361eeda0SAdrian Hunter #define O2_SD_ADMA2 0xE7 35096cc0cdSChevron Li #define O2_SD_MISC_CTRL2 0xF0 36361eeda0SAdrian Hunter #define O2_SD_INF_MOD 0xF1 37361eeda0SAdrian Hunter #define O2_SD_MISC_CTRL4 0xFC 381ad9f880SShirley Her #define O2_SD_MISC_CTRL 0x1C0 39*3d757ddbSChevron Li #define O2_SD_EXP_INT_REG 0x1E0 401ad9f880SShirley Her #define O2_SD_PWR_FORCE_L0 0x0002 41361eeda0SAdrian Hunter #define O2_SD_TUNING_CTRL 0x300 42361eeda0SAdrian Hunter #define O2_SD_PLL_SETTING 0x304 4357322d54Sernest.zhang #define O2_SD_MISC_SETTING 0x308 44361eeda0SAdrian Hunter #define O2_SD_CLK_SETTING 0x328 45361eeda0SAdrian Hunter #define O2_SD_CAP_REG2 0x330 46361eeda0SAdrian Hunter #define O2_SD_CAP_REG0 0x334 47361eeda0SAdrian Hunter #define O2_SD_UHS1_CAP_SETTING 0x33C 48361eeda0SAdrian Hunter #define O2_SD_DELAY_CTRL 0x350 494be33cf1SFred Ai #define O2_SD_OUTPUT_CLK_SOURCE_SWITCH 0x354 50361eeda0SAdrian Hunter #define O2_SD_UHS2_L1_CTRL 0x35C 51361eeda0SAdrian Hunter #define O2_SD_FUNC_REG3 0x3E0 52361eeda0SAdrian Hunter #define O2_SD_FUNC_REG4 0x3E4 53*3d757ddbSChevron Li #define O2_SD_PARA_SET_REG1 0x444 54*3d757ddbSChevron Li #define O2_SD_VDDX_CTRL_REG 0x508 55*3d757ddbSChevron Li #define O2_SD_GPIO_CTRL_REG1 0x510 56361eeda0SAdrian Hunter #define O2_SD_LED_ENABLE BIT(6) 57361eeda0SAdrian Hunter #define O2_SD_FREG0_LEDOFF BIT(13) 584be33cf1SFred Ai #define O2_SD_SEL_DLL BIT(16) 59361eeda0SAdrian Hunter #define O2_SD_FREG4_ENABLE_CLK_SET BIT(22) 604be33cf1SFred Ai #define O2_SD_PHASE_MASK GENMASK(23, 20) 614be33cf1SFred Ai #define O2_SD_FIX_PHASE FIELD_PREP(O2_SD_PHASE_MASK, 0x9) 62361eeda0SAdrian Hunter 63361eeda0SAdrian Hunter #define O2_SD_VENDOR_SETTING 0x110 64361eeda0SAdrian Hunter #define O2_SD_VENDOR_SETTING2 0x1C8 650086fc21Sernest.zhang #define O2_SD_HW_TUNING_DISABLE BIT(4) 660086fc21Sernest.zhang 679674bab4SShirley Her (SC) #define O2_PLL_DLL_WDT_CONTROL1 0x1CC 6869d91ed1SErnest Zhang(WH) #define O2_PLL_FORCE_ACTIVE BIT(18) 6969d91ed1SErnest Zhang(WH) #define O2_PLL_LOCK_STATUS BIT(14) 7069d91ed1SErnest Zhang(WH) #define O2_PLL_SOFT_RESET BIT(12) 717d440617SShirley Her (SC) #define O2_DLL_LOCK_STATUS BIT(11) 7269d91ed1SErnest Zhang(WH) 7369d91ed1SErnest Zhang(WH) #define O2_SD_DETECT_SETTING 0x324 7469d91ed1SErnest Zhang(WH) 757d440617SShirley Her (SC) static const u32 dmdn_table[] = {0x2B1C0000, 767d440617SShirley Her (SC) 0x2C1A0000, 0x371B0000, 0x35100000}; 777d440617SShirley Her (SC) #define DMDN_SZ ARRAY_SIZE(dmdn_table) 787d440617SShirley Her (SC) 797d440617SShirley Her (SC) struct o2_host { 807d440617SShirley Her (SC) u8 dll_adjust_count; 817d440617SShirley Her (SC) }; 827d440617SShirley Her (SC) 83908fd508SShirley Her (SC) static void sdhci_o2_wait_card_detect_stable(struct sdhci_host *host) 84908fd508SShirley Her (SC) { 85908fd508SShirley Her (SC) ktime_t timeout; 86908fd508SShirley Her (SC) u32 scratch32; 87908fd508SShirley Her (SC) 88908fd508SShirley Her (SC) /* Wait max 50 ms */ 89908fd508SShirley Her (SC) timeout = ktime_add_ms(ktime_get(), 50); 90908fd508SShirley Her (SC) while (1) { 91908fd508SShirley Her (SC) bool timedout = ktime_after(ktime_get(), timeout); 92908fd508SShirley Her (SC) 93908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, SDHCI_PRESENT_STATE); 94908fd508SShirley Her (SC) if ((scratch32 & SDHCI_CARD_PRESENT) >> SDHCI_CARD_PRES_SHIFT 95908fd508SShirley Her (SC) == (scratch32 & SDHCI_CD_LVL) >> SDHCI_CD_LVL_SHIFT) 96908fd508SShirley Her (SC) break; 97908fd508SShirley Her (SC) 98908fd508SShirley Her (SC) if (timedout) { 99908fd508SShirley Her (SC) pr_err("%s: Card Detect debounce never finished.\n", 100908fd508SShirley Her (SC) mmc_hostname(host->mmc)); 101908fd508SShirley Her (SC) sdhci_dumpregs(host); 102908fd508SShirley Her (SC) return; 103908fd508SShirley Her (SC) } 104908fd508SShirley Her (SC) udelay(10); 105908fd508SShirley Her (SC) } 106908fd508SShirley Her (SC) } 107908fd508SShirley Her (SC) 108908fd508SShirley Her (SC) static void sdhci_o2_enable_internal_clock(struct sdhci_host *host) 109908fd508SShirley Her (SC) { 110908fd508SShirley Her (SC) ktime_t timeout; 111908fd508SShirley Her (SC) u16 scratch; 112908fd508SShirley Her (SC) u32 scratch32; 113908fd508SShirley Her (SC) 114908fd508SShirley Her (SC) /* PLL software reset */ 115908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 116908fd508SShirley Her (SC) scratch32 |= O2_PLL_SOFT_RESET; 117908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 118908fd508SShirley Her (SC) udelay(1); 119908fd508SShirley Her (SC) scratch32 &= ~(O2_PLL_SOFT_RESET); 120908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 121908fd508SShirley Her (SC) 122908fd508SShirley Her (SC) /* PLL force active */ 123908fd508SShirley Her (SC) scratch32 |= O2_PLL_FORCE_ACTIVE; 124908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 125908fd508SShirley Her (SC) 126908fd508SShirley Her (SC) /* Wait max 20 ms */ 127908fd508SShirley Her (SC) timeout = ktime_add_ms(ktime_get(), 20); 128908fd508SShirley Her (SC) while (1) { 129908fd508SShirley Her (SC) bool timedout = ktime_after(ktime_get(), timeout); 130908fd508SShirley Her (SC) 131908fd508SShirley Her (SC) scratch = sdhci_readw(host, O2_PLL_DLL_WDT_CONTROL1); 132908fd508SShirley Her (SC) if (scratch & O2_PLL_LOCK_STATUS) 133908fd508SShirley Her (SC) break; 134908fd508SShirley Her (SC) if (timedout) { 135908fd508SShirley Her (SC) pr_err("%s: Internal clock never stabilised.\n", 136908fd508SShirley Her (SC) mmc_hostname(host->mmc)); 137908fd508SShirley Her (SC) sdhci_dumpregs(host); 138908fd508SShirley Her (SC) goto out; 139908fd508SShirley Her (SC) } 140908fd508SShirley Her (SC) udelay(10); 141908fd508SShirley Her (SC) } 142908fd508SShirley Her (SC) 143908fd508SShirley Her (SC) /* Wait for card detect finish */ 144908fd508SShirley Her (SC) udelay(1); 145908fd508SShirley Her (SC) sdhci_o2_wait_card_detect_stable(host); 146908fd508SShirley Her (SC) 147908fd508SShirley Her (SC) out: 148908fd508SShirley Her (SC) /* Cancel PLL force active */ 149908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 150908fd508SShirley Her (SC) scratch32 &= ~O2_PLL_FORCE_ACTIVE; 151908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 152908fd508SShirley Her (SC) } 153908fd508SShirley Her (SC) 154908fd508SShirley Her (SC) static int sdhci_o2_get_cd(struct mmc_host *mmc) 155908fd508SShirley Her (SC) { 156908fd508SShirley Her (SC) struct sdhci_host *host = mmc_priv(mmc); 157908fd508SShirley Her (SC) 1587d440617SShirley Her (SC) if (!(sdhci_readw(host, O2_PLL_DLL_WDT_CONTROL1) & O2_PLL_LOCK_STATUS)) 159908fd508SShirley Her (SC) sdhci_o2_enable_internal_clock(host); 160e591fcf6SChevron Li else 161e591fcf6SChevron Li sdhci_o2_wait_card_detect_stable(host); 162908fd508SShirley Her (SC) 163908fd508SShirley Her (SC) return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); 164908fd508SShirley Her (SC) } 165908fd508SShirley Her (SC) 166908fd508SShirley Her (SC) static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) 167908fd508SShirley Her (SC) { 168908fd508SShirley Her (SC) u32 scratch_32; 169908fd508SShirley Her (SC) 170908fd508SShirley Her (SC) pci_read_config_dword(chip->pdev, 171908fd508SShirley Her (SC) O2_SD_PLL_SETTING, &scratch_32); 172908fd508SShirley Her (SC) 173908fd508SShirley Her (SC) scratch_32 &= 0x0000FFFF; 174908fd508SShirley Her (SC) scratch_32 |= value; 175908fd508SShirley Her (SC) 176908fd508SShirley Her (SC) pci_write_config_dword(chip->pdev, 177908fd508SShirley Her (SC) O2_SD_PLL_SETTING, scratch_32); 178908fd508SShirley Her (SC) } 179908fd508SShirley Her (SC) 1807d440617SShirley Her (SC) static u32 sdhci_o2_pll_dll_wdt_control(struct sdhci_host *host) 1817d440617SShirley Her (SC) { 1827d440617SShirley Her (SC) return sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 1837d440617SShirley Her (SC) } 1847d440617SShirley Her (SC) 1857d440617SShirley Her (SC) /* 1867d440617SShirley Her (SC) * This function is used to detect dll lock status. 1877d440617SShirley Her (SC) * Since the dll lock status bit will toggle randomly 1887d440617SShirley Her (SC) * with very short interval which needs to be polled 1897d440617SShirley Her (SC) * as fast as possible. Set sleep_us as 1 microsecond. 1907d440617SShirley Her (SC) */ 1917d440617SShirley Her (SC) static int sdhci_o2_wait_dll_detect_lock(struct sdhci_host *host) 1927d440617SShirley Her (SC) { 1937d440617SShirley Her (SC) u32 scratch32 = 0; 1947d440617SShirley Her (SC) 1957d440617SShirley Her (SC) return readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host, 1967d440617SShirley Her (SC) scratch32, !(scratch32 & O2_DLL_LOCK_STATUS), 1, 1000000); 1977d440617SShirley Her (SC) } 1987d440617SShirley Her (SC) 1990086fc21Sernest.zhang static void sdhci_o2_set_tuning_mode(struct sdhci_host *host) 2000086fc21Sernest.zhang { 2010086fc21Sernest.zhang u16 reg; 2020086fc21Sernest.zhang 2030086fc21Sernest.zhang /* enable hardware tuning */ 2040086fc21Sernest.zhang reg = sdhci_readw(host, O2_SD_VENDOR_SETTING); 2050086fc21Sernest.zhang reg &= ~O2_SD_HW_TUNING_DISABLE; 2060086fc21Sernest.zhang sdhci_writew(host, reg, O2_SD_VENDOR_SETTING); 2070086fc21Sernest.zhang } 2080086fc21Sernest.zhang 2090086fc21Sernest.zhang static void __sdhci_o2_execute_tuning(struct sdhci_host *host, u32 opcode) 2100086fc21Sernest.zhang { 2110086fc21Sernest.zhang int i; 2120086fc21Sernest.zhang 2137b7d897eSshirley her sdhci_send_tuning(host, opcode); 2140086fc21Sernest.zhang 2150086fc21Sernest.zhang for (i = 0; i < 150; i++) { 2160086fc21Sernest.zhang u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 2170086fc21Sernest.zhang 2180086fc21Sernest.zhang if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { 2190086fc21Sernest.zhang if (ctrl & SDHCI_CTRL_TUNED_CLK) { 2200086fc21Sernest.zhang host->tuning_done = true; 2210086fc21Sernest.zhang return; 2220086fc21Sernest.zhang } 2230086fc21Sernest.zhang pr_warn("%s: HW tuning failed !\n", 2240086fc21Sernest.zhang mmc_hostname(host->mmc)); 2250086fc21Sernest.zhang break; 2260086fc21Sernest.zhang } 2270086fc21Sernest.zhang 2280086fc21Sernest.zhang mdelay(1); 2290086fc21Sernest.zhang } 2300086fc21Sernest.zhang 2310086fc21Sernest.zhang pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", 2320086fc21Sernest.zhang mmc_hostname(host->mmc)); 2330086fc21Sernest.zhang sdhci_reset_tuning(host); 2340086fc21Sernest.zhang } 2350086fc21Sernest.zhang 2367d440617SShirley Her (SC) /* 2377d440617SShirley Her (SC) * This function is used to fix o2 dll shift issue. 2387d440617SShirley Her (SC) * It isn't necessary to detect card present before recovery. 2397d440617SShirley Her (SC) * Firstly, it is used by bht emmc card, which is embedded. 2407d440617SShirley Her (SC) * Second, before call recovery card present will be detected 2417d440617SShirley Her (SC) * outside of the execute tuning function. 2427d440617SShirley Her (SC) */ 2437d440617SShirley Her (SC) static int sdhci_o2_dll_recovery(struct sdhci_host *host) 2447d440617SShirley Her (SC) { 2457d440617SShirley Her (SC) int ret = 0; 2467d440617SShirley Her (SC) u8 scratch_8 = 0; 2477d440617SShirley Her (SC) u32 scratch_32 = 0; 2487d440617SShirley Her (SC) struct sdhci_pci_slot *slot = sdhci_priv(host); 2497d440617SShirley Her (SC) struct sdhci_pci_chip *chip = slot->chip; 2507d440617SShirley Her (SC) struct o2_host *o2_host = sdhci_pci_priv(slot); 2517d440617SShirley Her (SC) 2527d440617SShirley Her (SC) /* UnLock WP */ 2537d440617SShirley Her (SC) pci_read_config_byte(chip->pdev, 2547d440617SShirley Her (SC) O2_SD_LOCK_WP, &scratch_8); 2557d440617SShirley Her (SC) scratch_8 &= 0x7f; 2567d440617SShirley Her (SC) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 2577d440617SShirley Her (SC) while (o2_host->dll_adjust_count < DMDN_SZ && !ret) { 2587d440617SShirley Her (SC) /* Disable clock */ 2597d440617SShirley Her (SC) sdhci_writeb(host, 0, SDHCI_CLOCK_CONTROL); 2607d440617SShirley Her (SC) 2617d440617SShirley Her (SC) /* PLL software reset */ 2627d440617SShirley Her (SC) scratch_32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 2637d440617SShirley Her (SC) scratch_32 |= O2_PLL_SOFT_RESET; 2647d440617SShirley Her (SC) sdhci_writel(host, scratch_32, O2_PLL_DLL_WDT_CONTROL1); 2657d440617SShirley Her (SC) 2667d440617SShirley Her (SC) pci_read_config_dword(chip->pdev, 2677d440617SShirley Her (SC) O2_SD_FUNC_REG4, 2687d440617SShirley Her (SC) &scratch_32); 2697d440617SShirley Her (SC) /* Enable Base Clk setting change */ 2707d440617SShirley Her (SC) scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; 2717d440617SShirley Her (SC) pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG4, scratch_32); 2727d440617SShirley Her (SC) o2_pci_set_baseclk(chip, dmdn_table[o2_host->dll_adjust_count]); 2737d440617SShirley Her (SC) 2747d440617SShirley Her (SC) /* Enable internal clock */ 2757d440617SShirley Her (SC) scratch_8 = SDHCI_CLOCK_INT_EN; 2767d440617SShirley Her (SC) sdhci_writeb(host, scratch_8, SDHCI_CLOCK_CONTROL); 2777d440617SShirley Her (SC) 2787d440617SShirley Her (SC) if (sdhci_o2_get_cd(host->mmc)) { 2797d440617SShirley Her (SC) /* 2807d440617SShirley Her (SC) * need wait at least 5ms for dll status stable, 2817d440617SShirley Her (SC) * after enable internal clock 2827d440617SShirley Her (SC) */ 2837d440617SShirley Her (SC) usleep_range(5000, 6000); 2847d440617SShirley Her (SC) if (sdhci_o2_wait_dll_detect_lock(host)) { 2857d440617SShirley Her (SC) scratch_8 |= SDHCI_CLOCK_CARD_EN; 2867d440617SShirley Her (SC) sdhci_writeb(host, scratch_8, 2877d440617SShirley Her (SC) SDHCI_CLOCK_CONTROL); 2887d440617SShirley Her (SC) ret = 1; 2897d440617SShirley Her (SC) } else { 2907d440617SShirley Her (SC) pr_warn("%s: DLL unlocked when dll_adjust_count is %d.\n", 2917d440617SShirley Her (SC) mmc_hostname(host->mmc), 2927d440617SShirley Her (SC) o2_host->dll_adjust_count); 2937d440617SShirley Her (SC) } 2947d440617SShirley Her (SC) } else { 2957d440617SShirley Her (SC) pr_err("%s: card present detect failed.\n", 2967d440617SShirley Her (SC) mmc_hostname(host->mmc)); 2977d440617SShirley Her (SC) break; 2987d440617SShirley Her (SC) } 2997d440617SShirley Her (SC) 3007d440617SShirley Her (SC) o2_host->dll_adjust_count++; 3017d440617SShirley Her (SC) } 3027d440617SShirley Her (SC) if (!ret && o2_host->dll_adjust_count == DMDN_SZ) 3037d440617SShirley Her (SC) pr_err("%s: DLL adjust over max times\n", 3047d440617SShirley Her (SC) mmc_hostname(host->mmc)); 3057d440617SShirley Her (SC) /* Lock WP */ 3067d440617SShirley Her (SC) pci_read_config_byte(chip->pdev, 3077d440617SShirley Her (SC) O2_SD_LOCK_WP, &scratch_8); 3087d440617SShirley Her (SC) scratch_8 |= 0x80; 3097d440617SShirley Her (SC) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 3107d440617SShirley Her (SC) return ret; 3117d440617SShirley Her (SC) } 3127d440617SShirley Her (SC) 3130086fc21Sernest.zhang static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) 3140086fc21Sernest.zhang { 3150086fc21Sernest.zhang struct sdhci_host *host = mmc_priv(mmc); 3164be33cf1SFred Ai struct sdhci_pci_slot *slot = sdhci_priv(host); 3174be33cf1SFred Ai struct sdhci_pci_chip *chip = slot->chip; 3180086fc21Sernest.zhang int current_bus_width = 0; 3191ad9f880SShirley Her u32 scratch32 = 0; 3201ad9f880SShirley Her u16 scratch = 0; 3214be33cf1SFred Ai u8 scratch_8 = 0; 3224be33cf1SFred Ai u32 reg_val; 3230086fc21Sernest.zhang 3240086fc21Sernest.zhang /* 32550aeccccSChevron Li * This handler implements the hardware tuning that is specific to 3260086fc21Sernest.zhang * this controller. Fall back to the standard method for other TIMING. 3270086fc21Sernest.zhang */ 3287b7d897eSshirley her if ((host->timing != MMC_TIMING_MMC_HS200) && 32950aeccccSChevron Li (host->timing != MMC_TIMING_UHS_SDR104) && 33050aeccccSChevron Li (host->timing != MMC_TIMING_UHS_SDR50)) 3310086fc21Sernest.zhang return sdhci_execute_tuning(mmc, opcode); 3320086fc21Sernest.zhang 333b98e7e8dSChanWoo Lee if (WARN_ON(!mmc_op_tuning(opcode))) 3340086fc21Sernest.zhang return -EINVAL; 3351ad9f880SShirley Her 3361ad9f880SShirley Her /* Force power mode enter L0 */ 3371ad9f880SShirley Her scratch = sdhci_readw(host, O2_SD_MISC_CTRL); 3381ad9f880SShirley Her scratch |= O2_SD_PWR_FORCE_L0; 3391ad9f880SShirley Her sdhci_writew(host, scratch, O2_SD_MISC_CTRL); 3401ad9f880SShirley Her 341*3d757ddbSChevron Li /* Update output phase */ 342*3d757ddbSChevron Li switch (chip->pdev->device) { 343*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_SDS0: 344*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_SEABIRD0: 345*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_SEABIRD1: 346*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_SDS1: 347*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_FUJIN2: 3484be33cf1SFred Ai /* Stop clk */ 3494be33cf1SFred Ai reg_val = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 3504be33cf1SFred Ai reg_val &= ~SDHCI_CLOCK_CARD_EN; 3514be33cf1SFred Ai sdhci_writew(host, reg_val, SDHCI_CLOCK_CONTROL); 3524be33cf1SFred Ai 353*3d757ddbSChevron Li if (host->timing == MMC_TIMING_MMC_HS200 || 354*3d757ddbSChevron Li host->timing == MMC_TIMING_UHS_SDR104) { 3554be33cf1SFred Ai /* UnLock WP */ 3564be33cf1SFred Ai pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); 3574be33cf1SFred Ai scratch_8 &= 0x7f; 3584be33cf1SFred Ai pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 3594be33cf1SFred Ai 3604be33cf1SFred Ai /* Set pcr 0x354[16] to choose dll clock, and set the default phase */ 3614be33cf1SFred Ai pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, ®_val); 3624be33cf1SFred Ai reg_val &= ~(O2_SD_SEL_DLL | O2_SD_PHASE_MASK); 3634be33cf1SFred Ai reg_val |= (O2_SD_SEL_DLL | O2_SD_FIX_PHASE); 3644be33cf1SFred Ai pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, reg_val); 3654be33cf1SFred Ai 3664be33cf1SFred Ai /* Lock WP */ 3674be33cf1SFred Ai pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); 3684be33cf1SFred Ai scratch_8 |= 0x80; 3694be33cf1SFred Ai pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 37051dfc614SFred } 371*3d757ddbSChevron Li 3724be33cf1SFred Ai /* Start clk */ 3734be33cf1SFred Ai reg_val = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 3744be33cf1SFred Ai reg_val |= SDHCI_CLOCK_CARD_EN; 3754be33cf1SFred Ai sdhci_writew(host, reg_val, SDHCI_CLOCK_CONTROL); 376*3d757ddbSChevron Li break; 377*3d757ddbSChevron Li default: 378*3d757ddbSChevron Li break; 379*3d757ddbSChevron Li } 3804be33cf1SFred Ai 3811ad9f880SShirley Her /* wait DLL lock, timeout value 5ms */ 3821ad9f880SShirley Her if (readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host, 3831ad9f880SShirley Her scratch32, (scratch32 & O2_DLL_LOCK_STATUS), 1, 5000)) 3841ad9f880SShirley Her pr_warn("%s: DLL can't lock in 5ms after force L0 during tuning.\n", 3851ad9f880SShirley Her mmc_hostname(host->mmc)); 3867d440617SShirley Her (SC) /* 3877d440617SShirley Her (SC) * Judge the tuning reason, whether caused by dll shift 3887d440617SShirley Her (SC) * If cause by dll shift, should call sdhci_o2_dll_recovery 3897d440617SShirley Her (SC) */ 3907d440617SShirley Her (SC) if (!sdhci_o2_wait_dll_detect_lock(host)) 3917d440617SShirley Her (SC) if (!sdhci_o2_dll_recovery(host)) { 3927d440617SShirley Her (SC) pr_err("%s: o2 dll recovery failed\n", 3937d440617SShirley Her (SC) mmc_hostname(host->mmc)); 3947d440617SShirley Her (SC) return -EINVAL; 3957d440617SShirley Her (SC) } 3960086fc21Sernest.zhang /* 3970086fc21Sernest.zhang * o2 sdhci host didn't support 8bit emmc tuning 3980086fc21Sernest.zhang */ 3990086fc21Sernest.zhang if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { 4000086fc21Sernest.zhang current_bus_width = mmc->ios.bus_width; 4010f7b79a4SRaul E Rangel mmc->ios.bus_width = MMC_BUS_WIDTH_4; 4020086fc21Sernest.zhang sdhci_set_bus_width(host, MMC_BUS_WIDTH_4); 4030086fc21Sernest.zhang } 4040086fc21Sernest.zhang 4050086fc21Sernest.zhang sdhci_o2_set_tuning_mode(host); 4060086fc21Sernest.zhang 4070086fc21Sernest.zhang sdhci_start_tuning(host); 4080086fc21Sernest.zhang 4090086fc21Sernest.zhang __sdhci_o2_execute_tuning(host, opcode); 4100086fc21Sernest.zhang 4110086fc21Sernest.zhang sdhci_end_tuning(host); 4120086fc21Sernest.zhang 4130f7b79a4SRaul E Rangel if (current_bus_width == MMC_BUS_WIDTH_8) { 4140f7b79a4SRaul E Rangel mmc->ios.bus_width = MMC_BUS_WIDTH_8; 4150086fc21Sernest.zhang sdhci_set_bus_width(host, current_bus_width); 4160f7b79a4SRaul E Rangel } 4170086fc21Sernest.zhang 4181ad9f880SShirley Her /* Cancel force power mode enter L0 */ 4191ad9f880SShirley Her scratch = sdhci_readw(host, O2_SD_MISC_CTRL); 4201ad9f880SShirley Her scratch &= ~(O2_SD_PWR_FORCE_L0); 4211ad9f880SShirley Her sdhci_writew(host, scratch, O2_SD_MISC_CTRL); 4221ad9f880SShirley Her 4237b7d897eSshirley her sdhci_reset(host, SDHCI_RESET_CMD); 4247b7d897eSshirley her sdhci_reset(host, SDHCI_RESET_DATA); 4257b7d897eSshirley her 4260086fc21Sernest.zhang host->flags &= ~SDHCI_HS400_TUNING; 4270086fc21Sernest.zhang return 0; 4280086fc21Sernest.zhang } 42901acf691SAdam Lee 430706adf6bSPeter Guo static void o2_pci_led_enable(struct sdhci_pci_chip *chip) 431706adf6bSPeter Guo { 432706adf6bSPeter Guo int ret; 433706adf6bSPeter Guo u32 scratch_32; 434706adf6bSPeter Guo 435706adf6bSPeter Guo /* Set led of SD host function enable */ 436706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 437706adf6bSPeter Guo O2_SD_FUNC_REG0, &scratch_32); 438706adf6bSPeter Guo if (ret) 439706adf6bSPeter Guo return; 440706adf6bSPeter Guo 441706adf6bSPeter Guo scratch_32 &= ~O2_SD_FREG0_LEDOFF; 442706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 443706adf6bSPeter Guo O2_SD_FUNC_REG0, scratch_32); 444706adf6bSPeter Guo 445706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 446706adf6bSPeter Guo O2_SD_TEST_REG, &scratch_32); 447706adf6bSPeter Guo if (ret) 448706adf6bSPeter Guo return; 449706adf6bSPeter Guo 450706adf6bSPeter Guo scratch_32 |= O2_SD_LED_ENABLE; 451706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 452706adf6bSPeter Guo O2_SD_TEST_REG, scratch_32); 453706adf6bSPeter Guo } 454706adf6bSPeter Guo 455f0cbd780SBen Hutchings static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) 45601acf691SAdam Lee { 45701acf691SAdam Lee u32 scratch_32; 45801acf691SAdam Lee int ret; 45901acf691SAdam Lee /* Improve write performance for SD3.0 */ 46001acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32); 46101acf691SAdam Lee if (ret) 46201acf691SAdam Lee return; 46301acf691SAdam Lee scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14)); 46401acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32); 46501acf691SAdam Lee 46601acf691SAdam Lee /* Enable Link abnormal reset generating Reset */ 46701acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32); 46801acf691SAdam Lee if (ret) 46901acf691SAdam Lee return; 47001acf691SAdam Lee scratch_32 &= ~((1 << 19) | (1 << 11)); 47101acf691SAdam Lee scratch_32 |= (1 << 10); 47201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32); 47301acf691SAdam Lee 47401acf691SAdam Lee /* set card power over current protection */ 47501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32); 47601acf691SAdam Lee if (ret) 47701acf691SAdam Lee return; 47801acf691SAdam Lee scratch_32 |= (1 << 4); 47901acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32); 48001acf691SAdam Lee 48101acf691SAdam Lee /* adjust the output delay for SD mode */ 48201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492); 48301acf691SAdam Lee 48401acf691SAdam Lee /* Set the output voltage setting of Aux 1.2v LDO */ 48501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32); 48601acf691SAdam Lee if (ret) 48701acf691SAdam Lee return; 48801acf691SAdam Lee scratch_32 &= ~(3 << 12); 48901acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32); 49001acf691SAdam Lee 49101acf691SAdam Lee /* Set Max power supply capability of SD host */ 49201acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32); 49301acf691SAdam Lee if (ret) 49401acf691SAdam Lee return; 49501acf691SAdam Lee scratch_32 &= ~(0x01FE); 49601acf691SAdam Lee scratch_32 |= 0x00CC; 49701acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32); 49801acf691SAdam Lee /* Set DLL Tuning Window */ 49901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 50001acf691SAdam Lee O2_SD_TUNING_CTRL, &scratch_32); 50101acf691SAdam Lee if (ret) 50201acf691SAdam Lee return; 50301acf691SAdam Lee scratch_32 &= ~(0x000000FF); 50401acf691SAdam Lee scratch_32 |= 0x00000066; 50501acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32); 50601acf691SAdam Lee 50701acf691SAdam Lee /* Set UHS2 T_EIDLE */ 50801acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 50901acf691SAdam Lee O2_SD_UHS2_L1_CTRL, &scratch_32); 51001acf691SAdam Lee if (ret) 51101acf691SAdam Lee return; 51201acf691SAdam Lee scratch_32 &= ~(0x000000FC); 51301acf691SAdam Lee scratch_32 |= 0x00000084; 51401acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32); 51501acf691SAdam Lee 51601acf691SAdam Lee /* Set UHS2 Termination */ 51701acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32); 51801acf691SAdam Lee if (ret) 51901acf691SAdam Lee return; 52001acf691SAdam Lee scratch_32 &= ~((1 << 21) | (1 << 30)); 52101acf691SAdam Lee 52201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); 52301acf691SAdam Lee 52401acf691SAdam Lee /* Set L1 Entrance Timer */ 52501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32); 52601acf691SAdam Lee if (ret) 52701acf691SAdam Lee return; 52801acf691SAdam Lee scratch_32 &= ~(0xf0000000); 52901acf691SAdam Lee scratch_32 |= 0x30000000; 53001acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32); 53101acf691SAdam Lee 53201acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 53301acf691SAdam Lee O2_SD_MISC_CTRL4, &scratch_32); 53401acf691SAdam Lee if (ret) 53501acf691SAdam Lee return; 53601acf691SAdam Lee scratch_32 &= ~(0x000f0000); 53701acf691SAdam Lee scratch_32 |= 0x00080000; 53801acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); 53901acf691SAdam Lee } 54001acf691SAdam Lee 54102a3c0bdSernest.zhang static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip, 54202a3c0bdSernest.zhang struct sdhci_host *host) 54302a3c0bdSernest.zhang { 54402a3c0bdSernest.zhang int ret; 54502a3c0bdSernest.zhang 54602a3c0bdSernest.zhang ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI); 54702a3c0bdSernest.zhang if (!ret) { 5480818d197SColin Ian King pr_info("%s: unsupported MSI, use INTx irq\n", 54902a3c0bdSernest.zhang mmc_hostname(host->mmc)); 55002a3c0bdSernest.zhang return; 55102a3c0bdSernest.zhang } 55202a3c0bdSernest.zhang 55302a3c0bdSernest.zhang ret = pci_alloc_irq_vectors(chip->pdev, 1, 1, 55402a3c0bdSernest.zhang PCI_IRQ_MSI | PCI_IRQ_MSIX); 55502a3c0bdSernest.zhang if (ret < 0) { 55602a3c0bdSernest.zhang pr_err("%s: enable PCI MSI failed, err=%d\n", 55702a3c0bdSernest.zhang mmc_hostname(host->mmc), ret); 55802a3c0bdSernest.zhang return; 55902a3c0bdSernest.zhang } 56002a3c0bdSernest.zhang 56102a3c0bdSernest.zhang host->irq = pci_irq_vector(chip->pdev, 0); 56202a3c0bdSernest.zhang } 56302a3c0bdSernest.zhang 56469d91ed1SErnest Zhang(WH) static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk) 56569d91ed1SErnest Zhang(WH) { 56669d91ed1SErnest Zhang(WH) /* Enable internal clock */ 56769d91ed1SErnest Zhang(WH) clk |= SDHCI_CLOCK_INT_EN; 56869d91ed1SErnest Zhang(WH) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 56969d91ed1SErnest Zhang(WH) 5707d440617SShirley Her (SC) sdhci_o2_enable_internal_clock(host); 57169d91ed1SErnest Zhang(WH) if (sdhci_o2_get_cd(host->mmc)) { 57269d91ed1SErnest Zhang(WH) clk |= SDHCI_CLOCK_CARD_EN; 57369d91ed1SErnest Zhang(WH) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 57469d91ed1SErnest Zhang(WH) } 57569d91ed1SErnest Zhang(WH) } 57669d91ed1SErnest Zhang(WH) 577580b946eSZou Wei static void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock) 57869d91ed1SErnest Zhang(WH) { 57969d91ed1SErnest Zhang(WH) u16 clk; 5807b7d897eSshirley her u8 scratch; 5817b7d897eSshirley her u32 scratch_32; 582*3d757ddbSChevron Li u32 dmdn_208m, dmdn_200m; 5837b7d897eSshirley her struct sdhci_pci_slot *slot = sdhci_priv(host); 5847b7d897eSshirley her struct sdhci_pci_chip *chip = slot->chip; 58569d91ed1SErnest Zhang(WH) 58669d91ed1SErnest Zhang(WH) host->mmc->actual_clock = 0; 58769d91ed1SErnest Zhang(WH) 58869d91ed1SErnest Zhang(WH) sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 58969d91ed1SErnest Zhang(WH) 59069d91ed1SErnest Zhang(WH) if (clock == 0) 59169d91ed1SErnest Zhang(WH) return; 59269d91ed1SErnest Zhang(WH) 5934be33cf1SFred Ai /* UnLock WP */ 5947b7d897eSshirley her pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); 5957b7d897eSshirley her scratch &= 0x7f; 5967b7d897eSshirley her pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 5977b7d897eSshirley her 598*3d757ddbSChevron Li if (chip->pdev->device == PCI_DEVICE_ID_O2_GG8_9860 || 599*3d757ddbSChevron Li chip->pdev->device == PCI_DEVICE_ID_O2_GG8_9861 || 600*3d757ddbSChevron Li chip->pdev->device == PCI_DEVICE_ID_O2_GG8_9862 || 601*3d757ddbSChevron Li chip->pdev->device == PCI_DEVICE_ID_O2_GG8_9863) { 602*3d757ddbSChevron Li dmdn_208m = 0x2c500000; 603*3d757ddbSChevron Li dmdn_200m = 0x25200000; 604*3d757ddbSChevron Li } else { 605*3d757ddbSChevron Li dmdn_208m = 0x2c280000; 606*3d757ddbSChevron Li dmdn_200m = 0x25100000; 607*3d757ddbSChevron Li } 608*3d757ddbSChevron Li 6094be33cf1SFred Ai if ((host->timing == MMC_TIMING_UHS_SDR104) && (clock == 200000000)) { 6107b7d897eSshirley her pci_read_config_dword(chip->pdev, O2_SD_PLL_SETTING, &scratch_32); 6117b7d897eSshirley her 612*3d757ddbSChevron Li if ((scratch_32 & 0xFFFF0000) != dmdn_208m) 613*3d757ddbSChevron Li o2_pci_set_baseclk(chip, dmdn_208m); 614a7c18e5cSFred Ai } else { 615a7c18e5cSFred Ai pci_read_config_dword(chip->pdev, O2_SD_PLL_SETTING, &scratch_32); 616a7c18e5cSFred Ai 617*3d757ddbSChevron Li if ((scratch_32 & 0xFFFF0000) != dmdn_200m) 618*3d757ddbSChevron Li o2_pci_set_baseclk(chip, dmdn_200m); 6194be33cf1SFred Ai } 6207b7d897eSshirley her 6214be33cf1SFred Ai pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, &scratch_32); 6224be33cf1SFred Ai scratch_32 &= ~(O2_SD_SEL_DLL | O2_SD_PHASE_MASK); 6234be33cf1SFred Ai pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, scratch_32); 6244be33cf1SFred Ai 6254be33cf1SFred Ai /* Lock WP */ 6267b7d897eSshirley her pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); 6277b7d897eSshirley her scratch |= 0x80; 6287b7d897eSshirley her pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 6297b7d897eSshirley her 63069d91ed1SErnest Zhang(WH) clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 63169d91ed1SErnest Zhang(WH) sdhci_o2_enable_clk(host, clk); 63269d91ed1SErnest Zhang(WH) } 63369d91ed1SErnest Zhang(WH) 634580b946eSZou Wei static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) 63501acf691SAdam Lee { 63601acf691SAdam Lee struct sdhci_pci_chip *chip; 63701acf691SAdam Lee struct sdhci_host *host; 6387d440617SShirley Her (SC) struct o2_host *o2_host = sdhci_pci_priv(slot); 639de23f0b7SRaul E Rangel u32 reg, caps; 64057322d54Sernest.zhang int ret; 64101acf691SAdam Lee 64201acf691SAdam Lee chip = slot->chip; 64301acf691SAdam Lee host = slot->host; 644de23f0b7SRaul E Rangel 6457d440617SShirley Her (SC) o2_host->dll_adjust_count = 0; 646de23f0b7SRaul E Rangel caps = sdhci_readl(host, SDHCI_CAPABILITIES); 647de23f0b7SRaul E Rangel 648de23f0b7SRaul E Rangel /* 649de23f0b7SRaul E Rangel * mmc_select_bus_width() will test the bus to determine the actual bus 650de23f0b7SRaul E Rangel * width. 651de23f0b7SRaul E Rangel */ 652de23f0b7SRaul E Rangel if (caps & SDHCI_CAN_DO_8BIT) 653de23f0b7SRaul E Rangel host->mmc->caps |= MMC_CAP_8_BIT_DATA; 654de23f0b7SRaul E Rangel 655*3d757ddbSChevron Li host->quirks2 |= SDHCI_QUIRK2_BROKEN_DDR50; 656*3d757ddbSChevron Li 657*3d757ddbSChevron Li sdhci_pci_o2_enable_msi(chip, host); 658*3d757ddbSChevron Li 659*3d757ddbSChevron Li host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning; 66001acf691SAdam Lee switch (chip->pdev->device) { 66101acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS0: 66201acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD0: 66301acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD1: 66401acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS1: 66501acf691SAdam Lee case PCI_DEVICE_ID_O2_FUJIN2: 66601acf691SAdam Lee reg = sdhci_readl(host, O2_SD_VENDOR_SETTING); 66701acf691SAdam Lee if (reg & 0x1) 66801acf691SAdam Lee host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; 66901acf691SAdam Lee 67057322d54Sernest.zhang if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) { 67157322d54Sernest.zhang ret = pci_read_config_dword(chip->pdev, 67257322d54Sernest.zhang O2_SD_MISC_SETTING, ®); 67357322d54Sernest.zhang if (ret) 67457322d54Sernest.zhang return -EIO; 67557322d54Sernest.zhang if (reg & (1 << 4)) { 67657322d54Sernest.zhang pr_info("%s: emmc 1.8v flag is set, force 1.8v signaling voltage\n", 67757322d54Sernest.zhang mmc_hostname(host->mmc)); 67857322d54Sernest.zhang host->flags &= ~SDHCI_SIGNALING_330; 67957322d54Sernest.zhang host->flags |= SDHCI_SIGNALING_180; 68057322d54Sernest.zhang host->mmc->caps2 |= MMC_CAP2_NO_SD; 68157322d54Sernest.zhang host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 68269d91ed1SErnest Zhang(WH) pci_write_config_dword(chip->pdev, 68369d91ed1SErnest Zhang(WH) O2_SD_DETECT_SETTING, 3); 68457322d54Sernest.zhang } 68569d91ed1SErnest Zhang(WH) 68669d91ed1SErnest Zhang(WH) slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd; 68757322d54Sernest.zhang } 68857322d54Sernest.zhang 689cdd2b769Sshirley her if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD1) { 690cdd2b769Sshirley her slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd; 691cdd2b769Sshirley her host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 692cdd2b769Sshirley her host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 693cdd2b769Sshirley her } 694cdd2b769Sshirley her 69501acf691SAdam Lee if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) 69601acf691SAdam Lee break; 69701acf691SAdam Lee /* set dll watch dog timer */ 69801acf691SAdam Lee reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2); 69901acf691SAdam Lee reg |= (1 << 12); 70001acf691SAdam Lee sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2); 701*3d757ddbSChevron Li break; 702*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9860: 703*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9861: 704*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9862: 705*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9863: 706*3d757ddbSChevron Li host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 707*3d757ddbSChevron Li host->mmc->caps |= MMC_CAP_HW_RESET; 708*3d757ddbSChevron Li host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 709*3d757ddbSChevron Li slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd; 71001acf691SAdam Lee break; 71101acf691SAdam Lee default: 71201acf691SAdam Lee break; 71301acf691SAdam Lee } 71401acf691SAdam Lee 71501acf691SAdam Lee return 0; 71601acf691SAdam Lee } 71701acf691SAdam Lee 718580b946eSZou Wei static int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) 71901acf691SAdam Lee { 72001acf691SAdam Lee int ret; 72101acf691SAdam Lee u8 scratch; 722*3d757ddbSChevron Li u16 scratch16; 72301acf691SAdam Lee u32 scratch_32; 72401acf691SAdam Lee 72501acf691SAdam Lee switch (chip->pdev->device) { 72601acf691SAdam Lee case PCI_DEVICE_ID_O2_8220: 72701acf691SAdam Lee case PCI_DEVICE_ID_O2_8221: 72801acf691SAdam Lee case PCI_DEVICE_ID_O2_8320: 72901acf691SAdam Lee case PCI_DEVICE_ID_O2_8321: 73001acf691SAdam Lee /* This extra setup is required due to broken ADMA. */ 73101acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 73201acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 73301acf691SAdam Lee if (ret) 73401acf691SAdam Lee return ret; 73501acf691SAdam Lee scratch &= 0x7f; 73601acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 73701acf691SAdam Lee 73801acf691SAdam Lee /* Set Multi 3 to VCC3V# */ 73901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); 74001acf691SAdam Lee 74101acf691SAdam Lee /* Disable CLK_REQ# support after media DET */ 74201acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 74301acf691SAdam Lee O2_SD_CLKREQ, &scratch); 74401acf691SAdam Lee if (ret) 74501acf691SAdam Lee return ret; 74601acf691SAdam Lee scratch |= 0x20; 74701acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); 74801acf691SAdam Lee 74901acf691SAdam Lee /* Choose capabilities, enable SDMA. We have to write 0x01 75001acf691SAdam Lee * to the capabilities register first to unlock it. 75101acf691SAdam Lee */ 75201acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); 75301acf691SAdam Lee if (ret) 75401acf691SAdam Lee return ret; 75501acf691SAdam Lee scratch |= 0x01; 75601acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); 75701acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); 75801acf691SAdam Lee 75901acf691SAdam Lee /* Disable ADMA1/2 */ 76001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); 76101acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); 76201acf691SAdam Lee 76301acf691SAdam Lee /* Disable the infinite transfer mode */ 76401acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 76501acf691SAdam Lee O2_SD_INF_MOD, &scratch); 76601acf691SAdam Lee if (ret) 76701acf691SAdam Lee return ret; 76801acf691SAdam Lee scratch |= 0x08; 76901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); 77001acf691SAdam Lee 77101acf691SAdam Lee /* Lock WP */ 77201acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 77301acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 77401acf691SAdam Lee if (ret) 77501acf691SAdam Lee return ret; 77601acf691SAdam Lee scratch |= 0x80; 77701acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 77801acf691SAdam Lee break; 77901acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS0: 78001acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS1: 78101acf691SAdam Lee case PCI_DEVICE_ID_O2_FUJIN2: 78201acf691SAdam Lee /* UnLock WP */ 78301acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 78401acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 78501acf691SAdam Lee if (ret) 78601acf691SAdam Lee return ret; 78701acf691SAdam Lee 78801acf691SAdam Lee scratch &= 0x7f; 78901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 79001acf691SAdam Lee 791706adf6bSPeter Guo /* DevId=8520 subId= 0x11 or 0x12 Type Chip support */ 792706adf6bSPeter Guo if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) { 793706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 794706adf6bSPeter Guo O2_SD_FUNC_REG0, 795706adf6bSPeter Guo &scratch_32); 796d599005aSDinghao Liu if (ret) 797d599005aSDinghao Liu return ret; 798706adf6bSPeter Guo scratch_32 = ((scratch_32 & 0xFF000000) >> 24); 799706adf6bSPeter Guo 800706adf6bSPeter Guo /* Check Whether subId is 0x11 or 0x12 */ 801706adf6bSPeter Guo if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { 8023665ff03Sernest.zhang scratch_32 = 0x25100000; 803706adf6bSPeter Guo 804706adf6bSPeter Guo o2_pci_set_baseclk(chip, scratch_32); 805706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 806706adf6bSPeter Guo O2_SD_FUNC_REG4, 807706adf6bSPeter Guo &scratch_32); 808d599005aSDinghao Liu if (ret) 809d599005aSDinghao Liu return ret; 810706adf6bSPeter Guo 811706adf6bSPeter Guo /* Enable Base Clk setting change */ 812706adf6bSPeter Guo scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; 813706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 814706adf6bSPeter Guo O2_SD_FUNC_REG4, 815706adf6bSPeter Guo scratch_32); 816706adf6bSPeter Guo 817706adf6bSPeter Guo /* Set Tuning Window to 4 */ 818706adf6bSPeter Guo pci_write_config_byte(chip->pdev, 819706adf6bSPeter Guo O2_SD_TUNING_CTRL, 0x44); 820706adf6bSPeter Guo 821706adf6bSPeter Guo break; 822706adf6bSPeter Guo } 823706adf6bSPeter Guo } 824706adf6bSPeter Guo 825706adf6bSPeter Guo /* Enable 8520 led function */ 826706adf6bSPeter Guo o2_pci_led_enable(chip); 827706adf6bSPeter Guo 82801acf691SAdam Lee /* Set timeout CLK */ 82901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 83001acf691SAdam Lee O2_SD_CLK_SETTING, &scratch_32); 83101acf691SAdam Lee if (ret) 83201acf691SAdam Lee return ret; 83301acf691SAdam Lee 83401acf691SAdam Lee scratch_32 &= ~(0xFF00); 83501acf691SAdam Lee scratch_32 |= 0x07E0C800; 83601acf691SAdam Lee pci_write_config_dword(chip->pdev, 83701acf691SAdam Lee O2_SD_CLK_SETTING, scratch_32); 83801acf691SAdam Lee 83901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 84001acf691SAdam Lee O2_SD_CLKREQ, &scratch_32); 84101acf691SAdam Lee if (ret) 84201acf691SAdam Lee return ret; 84301acf691SAdam Lee scratch_32 |= 0x3; 84401acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32); 84501acf691SAdam Lee 84601acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 84701acf691SAdam Lee O2_SD_PLL_SETTING, &scratch_32); 84801acf691SAdam Lee if (ret) 84901acf691SAdam Lee return ret; 85001acf691SAdam Lee 85101acf691SAdam Lee scratch_32 &= ~(0x1F3F070E); 85201acf691SAdam Lee scratch_32 |= 0x18270106; 85301acf691SAdam Lee pci_write_config_dword(chip->pdev, 85401acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 85501acf691SAdam Lee 85601acf691SAdam Lee /* Disable UHS1 funciton */ 85701acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 85801acf691SAdam Lee O2_SD_CAP_REG2, &scratch_32); 85901acf691SAdam Lee if (ret) 86001acf691SAdam Lee return ret; 86101acf691SAdam Lee scratch_32 &= ~(0xE0); 86201acf691SAdam Lee pci_write_config_dword(chip->pdev, 86301acf691SAdam Lee O2_SD_CAP_REG2, scratch_32); 86401acf691SAdam Lee 86501acf691SAdam Lee if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) 86601acf691SAdam Lee sdhci_pci_o2_fujin2_pci_init(chip); 86701acf691SAdam Lee 86801acf691SAdam Lee /* Lock WP */ 86901acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 87001acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 87101acf691SAdam Lee if (ret) 87201acf691SAdam Lee return ret; 87301acf691SAdam Lee scratch |= 0x80; 87401acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 87501acf691SAdam Lee break; 87601acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD0: 87701acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD1: 87801acf691SAdam Lee /* UnLock WP */ 87901acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 88001acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 88101acf691SAdam Lee if (ret) 88201acf691SAdam Lee return ret; 88301acf691SAdam Lee 88401acf691SAdam Lee scratch &= 0x7f; 88501acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 88601acf691SAdam Lee 88701acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 888706adf6bSPeter Guo O2_SD_PLL_SETTING, &scratch_32); 889d599005aSDinghao Liu if (ret) 890d599005aSDinghao Liu return ret; 89101acf691SAdam Lee 89201acf691SAdam Lee if ((scratch_32 & 0xff000000) == 0x01000000) { 89301acf691SAdam Lee scratch_32 &= 0x0000FFFF; 89401acf691SAdam Lee scratch_32 |= 0x1F340000; 89501acf691SAdam Lee 89601acf691SAdam Lee pci_write_config_dword(chip->pdev, 89701acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 89801acf691SAdam Lee } else { 89901acf691SAdam Lee scratch_32 &= 0x0000FFFF; 9003665ff03Sernest.zhang scratch_32 |= 0x25100000; 90101acf691SAdam Lee 90201acf691SAdam Lee pci_write_config_dword(chip->pdev, 90301acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 90401acf691SAdam Lee 90501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 90601acf691SAdam Lee O2_SD_FUNC_REG4, 90701acf691SAdam Lee &scratch_32); 908d599005aSDinghao Liu if (ret) 909d599005aSDinghao Liu return ret; 91001acf691SAdam Lee scratch_32 |= (1 << 22); 91101acf691SAdam Lee pci_write_config_dword(chip->pdev, 91201acf691SAdam Lee O2_SD_FUNC_REG4, scratch_32); 91301acf691SAdam Lee } 91401acf691SAdam Lee 915706adf6bSPeter Guo /* Set Tuning Windows to 5 */ 916706adf6bSPeter Guo pci_write_config_byte(chip->pdev, 917706adf6bSPeter Guo O2_SD_TUNING_CTRL, 0x55); 918096cc0cdSChevron Li //Adjust 1st and 2nd CD debounce time 919096cc0cdSChevron Li pci_read_config_dword(chip->pdev, O2_SD_MISC_CTRL2, &scratch_32); 920096cc0cdSChevron Li scratch_32 &= 0xFFE7FFFF; 921096cc0cdSChevron Li scratch_32 |= 0x00180000; 922096cc0cdSChevron Li pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL2, scratch_32); 923096cc0cdSChevron Li pci_write_config_dword(chip->pdev, O2_SD_DETECT_SETTING, 1); 92401acf691SAdam Lee /* Lock WP */ 92501acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 92601acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 92701acf691SAdam Lee if (ret) 92801acf691SAdam Lee return ret; 92901acf691SAdam Lee scratch |= 0x80; 93001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 93101acf691SAdam Lee break; 932*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9860: 933*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9861: 934*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9862: 935*3d757ddbSChevron Li case PCI_DEVICE_ID_O2_GG8_9863: 936*3d757ddbSChevron Li /* UnLock WP */ 937*3d757ddbSChevron Li ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); 938*3d757ddbSChevron Li if (ret) 939*3d757ddbSChevron Li return ret; 940*3d757ddbSChevron Li scratch &= 0x7f; 941*3d757ddbSChevron Li pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 942*3d757ddbSChevron Li 943*3d757ddbSChevron Li /* Select mode switch source as software control */ 944*3d757ddbSChevron Li pci_read_config_word(chip->pdev, O2_SD_PARA_SET_REG1, &scratch16); 945*3d757ddbSChevron Li scratch16 &= 0xF8FF; 946*3d757ddbSChevron Li scratch16 |= BIT(9); 947*3d757ddbSChevron Li pci_write_config_word(chip->pdev, O2_SD_PARA_SET_REG1, scratch16); 948*3d757ddbSChevron Li 949*3d757ddbSChevron Li /* set VDD1 supply source */ 950*3d757ddbSChevron Li pci_read_config_word(chip->pdev, O2_SD_VDDX_CTRL_REG, &scratch16); 951*3d757ddbSChevron Li scratch16 &= 0xFFE3; 952*3d757ddbSChevron Li scratch16 |= BIT(3); 953*3d757ddbSChevron Li pci_write_config_word(chip->pdev, O2_SD_VDDX_CTRL_REG, scratch16); 954*3d757ddbSChevron Li 955*3d757ddbSChevron Li /* Set host drive strength*/ 956*3d757ddbSChevron Li scratch16 = 0x0025; 957*3d757ddbSChevron Li pci_write_config_word(chip->pdev, O2_SD_PLL_SETTING, scratch16); 958*3d757ddbSChevron Li 959*3d757ddbSChevron Li /* Set output delay*/ 960*3d757ddbSChevron Li pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, &scratch_32); 961*3d757ddbSChevron Li scratch_32 &= 0xFF0FFF00; 962*3d757ddbSChevron Li scratch_32 |= 0x00B0003B; 963*3d757ddbSChevron Li pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, scratch_32); 964*3d757ddbSChevron Li 965*3d757ddbSChevron Li /* Lock WP */ 966*3d757ddbSChevron Li ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); 967*3d757ddbSChevron Li if (ret) 968*3d757ddbSChevron Li return ret; 969*3d757ddbSChevron Li scratch |= 0x80; 970*3d757ddbSChevron Li pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 971*3d757ddbSChevron Li break; 97201acf691SAdam Lee } 97301acf691SAdam Lee 97401acf691SAdam Lee return 0; 97501acf691SAdam Lee } 97601acf691SAdam Lee 977b7813f0fSAdrian Hunter #ifdef CONFIG_PM_SLEEP 978580b946eSZou Wei static int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) 97901acf691SAdam Lee { 98001acf691SAdam Lee sdhci_pci_o2_probe(chip); 98130cf2803SAdrian Hunter return sdhci_pci_resume_host(chip); 98201acf691SAdam Lee } 983b7813f0fSAdrian Hunter #endif 984328be8beSErnest Zhang(WH) 98569d91ed1SErnest Zhang(WH) static const struct sdhci_ops sdhci_pci_o2_ops = { 98669d91ed1SErnest Zhang(WH) .set_clock = sdhci_pci_o2_set_clock, 98769d91ed1SErnest Zhang(WH) .enable_dma = sdhci_pci_enable_dma, 98869d91ed1SErnest Zhang(WH) .set_bus_width = sdhci_set_bus_width, 98969d91ed1SErnest Zhang(WH) .reset = sdhci_reset, 99069d91ed1SErnest Zhang(WH) .set_uhs_signaling = sdhci_set_uhs_signaling, 99169d91ed1SErnest Zhang(WH) }; 99269d91ed1SErnest Zhang(WH) 993328be8beSErnest Zhang(WH) const struct sdhci_pci_fixes sdhci_o2 = { 994328be8beSErnest Zhang(WH) .probe = sdhci_pci_o2_probe, 995328be8beSErnest Zhang(WH) .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 99649baa01cSDaniel Drake .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, 997328be8beSErnest Zhang(WH) .probe_slot = sdhci_pci_o2_probe_slot, 998328be8beSErnest Zhang(WH) #ifdef CONFIG_PM_SLEEP 999328be8beSErnest Zhang(WH) .resume = sdhci_pci_o2_resume, 1000328be8beSErnest Zhang(WH) #endif 100169d91ed1SErnest Zhang(WH) .ops = &sdhci_pci_o2_ops, 10027d440617SShirley Her (SC) .priv_size = sizeof(struct o2_host), 1003328be8beSErnest Zhang(WH) }; 1004