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 391ad9f880SShirley Her #define O2_SD_PWR_FORCE_L0 0x0002 40361eeda0SAdrian Hunter #define O2_SD_TUNING_CTRL 0x300 41361eeda0SAdrian Hunter #define O2_SD_PLL_SETTING 0x304 4257322d54Sernest.zhang #define O2_SD_MISC_SETTING 0x308 43361eeda0SAdrian Hunter #define O2_SD_CLK_SETTING 0x328 44361eeda0SAdrian Hunter #define O2_SD_CAP_REG2 0x330 45361eeda0SAdrian Hunter #define O2_SD_CAP_REG0 0x334 46361eeda0SAdrian Hunter #define O2_SD_UHS1_CAP_SETTING 0x33C 47361eeda0SAdrian Hunter #define O2_SD_DELAY_CTRL 0x350 484be33cf1SFred Ai #define O2_SD_OUTPUT_CLK_SOURCE_SWITCH 0x354 49361eeda0SAdrian Hunter #define O2_SD_UHS2_L1_CTRL 0x35C 50361eeda0SAdrian Hunter #define O2_SD_FUNC_REG3 0x3E0 51361eeda0SAdrian Hunter #define O2_SD_FUNC_REG4 0x3E4 52361eeda0SAdrian Hunter #define O2_SD_LED_ENABLE BIT(6) 53361eeda0SAdrian Hunter #define O2_SD_FREG0_LEDOFF BIT(13) 544be33cf1SFred Ai #define O2_SD_SEL_DLL BIT(16) 55361eeda0SAdrian Hunter #define O2_SD_FREG4_ENABLE_CLK_SET BIT(22) 564be33cf1SFred Ai #define O2_SD_PHASE_MASK GENMASK(23, 20) 574be33cf1SFred Ai #define O2_SD_FIX_PHASE FIELD_PREP(O2_SD_PHASE_MASK, 0x9) 58361eeda0SAdrian Hunter 59361eeda0SAdrian Hunter #define O2_SD_VENDOR_SETTING 0x110 60361eeda0SAdrian Hunter #define O2_SD_VENDOR_SETTING2 0x1C8 610086fc21Sernest.zhang #define O2_SD_HW_TUNING_DISABLE BIT(4) 620086fc21Sernest.zhang 639674bab4SShirley Her (SC) #define O2_PLL_DLL_WDT_CONTROL1 0x1CC 6469d91ed1SErnest Zhang(WH) #define O2_PLL_FORCE_ACTIVE BIT(18) 6569d91ed1SErnest Zhang(WH) #define O2_PLL_LOCK_STATUS BIT(14) 6669d91ed1SErnest Zhang(WH) #define O2_PLL_SOFT_RESET BIT(12) 677d440617SShirley Her (SC) #define O2_DLL_LOCK_STATUS BIT(11) 6869d91ed1SErnest Zhang(WH) 6969d91ed1SErnest Zhang(WH) #define O2_SD_DETECT_SETTING 0x324 7069d91ed1SErnest Zhang(WH) 717d440617SShirley Her (SC) static const u32 dmdn_table[] = {0x2B1C0000, 727d440617SShirley Her (SC) 0x2C1A0000, 0x371B0000, 0x35100000}; 737d440617SShirley Her (SC) #define DMDN_SZ ARRAY_SIZE(dmdn_table) 747d440617SShirley Her (SC) 757d440617SShirley Her (SC) struct o2_host { 767d440617SShirley Her (SC) u8 dll_adjust_count; 777d440617SShirley Her (SC) }; 787d440617SShirley Her (SC) 79908fd508SShirley Her (SC) static void sdhci_o2_wait_card_detect_stable(struct sdhci_host *host) 80908fd508SShirley Her (SC) { 81908fd508SShirley Her (SC) ktime_t timeout; 82908fd508SShirley Her (SC) u32 scratch32; 83908fd508SShirley Her (SC) 84908fd508SShirley Her (SC) /* Wait max 50 ms */ 85908fd508SShirley Her (SC) timeout = ktime_add_ms(ktime_get(), 50); 86908fd508SShirley Her (SC) while (1) { 87908fd508SShirley Her (SC) bool timedout = ktime_after(ktime_get(), timeout); 88908fd508SShirley Her (SC) 89908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, SDHCI_PRESENT_STATE); 90908fd508SShirley Her (SC) if ((scratch32 & SDHCI_CARD_PRESENT) >> SDHCI_CARD_PRES_SHIFT 91908fd508SShirley Her (SC) == (scratch32 & SDHCI_CD_LVL) >> SDHCI_CD_LVL_SHIFT) 92908fd508SShirley Her (SC) break; 93908fd508SShirley Her (SC) 94908fd508SShirley Her (SC) if (timedout) { 95908fd508SShirley Her (SC) pr_err("%s: Card Detect debounce never finished.\n", 96908fd508SShirley Her (SC) mmc_hostname(host->mmc)); 97908fd508SShirley Her (SC) sdhci_dumpregs(host); 98908fd508SShirley Her (SC) return; 99908fd508SShirley Her (SC) } 100908fd508SShirley Her (SC) udelay(10); 101908fd508SShirley Her (SC) } 102908fd508SShirley Her (SC) } 103908fd508SShirley Her (SC) 104908fd508SShirley Her (SC) static void sdhci_o2_enable_internal_clock(struct sdhci_host *host) 105908fd508SShirley Her (SC) { 106908fd508SShirley Her (SC) ktime_t timeout; 107908fd508SShirley Her (SC) u16 scratch; 108908fd508SShirley Her (SC) u32 scratch32; 109908fd508SShirley Her (SC) 110908fd508SShirley Her (SC) /* PLL software reset */ 111908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 112908fd508SShirley Her (SC) scratch32 |= O2_PLL_SOFT_RESET; 113908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 114908fd508SShirley Her (SC) udelay(1); 115908fd508SShirley Her (SC) scratch32 &= ~(O2_PLL_SOFT_RESET); 116908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 117908fd508SShirley Her (SC) 118908fd508SShirley Her (SC) /* PLL force active */ 119908fd508SShirley Her (SC) scratch32 |= O2_PLL_FORCE_ACTIVE; 120908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 121908fd508SShirley Her (SC) 122908fd508SShirley Her (SC) /* Wait max 20 ms */ 123908fd508SShirley Her (SC) timeout = ktime_add_ms(ktime_get(), 20); 124908fd508SShirley Her (SC) while (1) { 125908fd508SShirley Her (SC) bool timedout = ktime_after(ktime_get(), timeout); 126908fd508SShirley Her (SC) 127908fd508SShirley Her (SC) scratch = sdhci_readw(host, O2_PLL_DLL_WDT_CONTROL1); 128908fd508SShirley Her (SC) if (scratch & O2_PLL_LOCK_STATUS) 129908fd508SShirley Her (SC) break; 130908fd508SShirley Her (SC) if (timedout) { 131908fd508SShirley Her (SC) pr_err("%s: Internal clock never stabilised.\n", 132908fd508SShirley Her (SC) mmc_hostname(host->mmc)); 133908fd508SShirley Her (SC) sdhci_dumpregs(host); 134908fd508SShirley Her (SC) goto out; 135908fd508SShirley Her (SC) } 136908fd508SShirley Her (SC) udelay(10); 137908fd508SShirley Her (SC) } 138908fd508SShirley Her (SC) 139908fd508SShirley Her (SC) /* Wait for card detect finish */ 140908fd508SShirley Her (SC) udelay(1); 141908fd508SShirley Her (SC) sdhci_o2_wait_card_detect_stable(host); 142908fd508SShirley Her (SC) 143908fd508SShirley Her (SC) out: 144908fd508SShirley Her (SC) /* Cancel PLL force active */ 145908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 146908fd508SShirley Her (SC) scratch32 &= ~O2_PLL_FORCE_ACTIVE; 147908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 148908fd508SShirley Her (SC) } 149908fd508SShirley Her (SC) 150908fd508SShirley Her (SC) static int sdhci_o2_get_cd(struct mmc_host *mmc) 151908fd508SShirley Her (SC) { 152908fd508SShirley Her (SC) struct sdhci_host *host = mmc_priv(mmc); 153908fd508SShirley Her (SC) 1547d440617SShirley Her (SC) if (!(sdhci_readw(host, O2_PLL_DLL_WDT_CONTROL1) & O2_PLL_LOCK_STATUS)) 155908fd508SShirley Her (SC) sdhci_o2_enable_internal_clock(host); 156e591fcf6SChevron Li else 157e591fcf6SChevron Li sdhci_o2_wait_card_detect_stable(host); 158908fd508SShirley Her (SC) 159908fd508SShirley Her (SC) return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); 160908fd508SShirley Her (SC) } 161908fd508SShirley Her (SC) 162908fd508SShirley Her (SC) static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) 163908fd508SShirley Her (SC) { 164908fd508SShirley Her (SC) u32 scratch_32; 165908fd508SShirley Her (SC) 166908fd508SShirley Her (SC) pci_read_config_dword(chip->pdev, 167908fd508SShirley Her (SC) O2_SD_PLL_SETTING, &scratch_32); 168908fd508SShirley Her (SC) 169908fd508SShirley Her (SC) scratch_32 &= 0x0000FFFF; 170908fd508SShirley Her (SC) scratch_32 |= value; 171908fd508SShirley Her (SC) 172908fd508SShirley Her (SC) pci_write_config_dword(chip->pdev, 173908fd508SShirley Her (SC) O2_SD_PLL_SETTING, scratch_32); 174908fd508SShirley Her (SC) } 175908fd508SShirley Her (SC) 1767d440617SShirley Her (SC) static u32 sdhci_o2_pll_dll_wdt_control(struct sdhci_host *host) 1777d440617SShirley Her (SC) { 1787d440617SShirley Her (SC) return sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 1797d440617SShirley Her (SC) } 1807d440617SShirley Her (SC) 1817d440617SShirley Her (SC) /* 1827d440617SShirley Her (SC) * This function is used to detect dll lock status. 1837d440617SShirley Her (SC) * Since the dll lock status bit will toggle randomly 1847d440617SShirley Her (SC) * with very short interval which needs to be polled 1857d440617SShirley Her (SC) * as fast as possible. Set sleep_us as 1 microsecond. 1867d440617SShirley Her (SC) */ 1877d440617SShirley Her (SC) static int sdhci_o2_wait_dll_detect_lock(struct sdhci_host *host) 1887d440617SShirley Her (SC) { 1897d440617SShirley Her (SC) u32 scratch32 = 0; 1907d440617SShirley Her (SC) 1917d440617SShirley Her (SC) return readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host, 1927d440617SShirley Her (SC) scratch32, !(scratch32 & O2_DLL_LOCK_STATUS), 1, 1000000); 1937d440617SShirley Her (SC) } 1947d440617SShirley Her (SC) 1950086fc21Sernest.zhang static void sdhci_o2_set_tuning_mode(struct sdhci_host *host) 1960086fc21Sernest.zhang { 1970086fc21Sernest.zhang u16 reg; 1980086fc21Sernest.zhang 1990086fc21Sernest.zhang /* enable hardware tuning */ 2000086fc21Sernest.zhang reg = sdhci_readw(host, O2_SD_VENDOR_SETTING); 2010086fc21Sernest.zhang reg &= ~O2_SD_HW_TUNING_DISABLE; 2020086fc21Sernest.zhang sdhci_writew(host, reg, O2_SD_VENDOR_SETTING); 2030086fc21Sernest.zhang } 2040086fc21Sernest.zhang 2050086fc21Sernest.zhang static void __sdhci_o2_execute_tuning(struct sdhci_host *host, u32 opcode) 2060086fc21Sernest.zhang { 2070086fc21Sernest.zhang int i; 2080086fc21Sernest.zhang 2097b7d897eSshirley her sdhci_send_tuning(host, opcode); 2100086fc21Sernest.zhang 2110086fc21Sernest.zhang for (i = 0; i < 150; i++) { 2120086fc21Sernest.zhang u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 2130086fc21Sernest.zhang 2140086fc21Sernest.zhang if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { 2150086fc21Sernest.zhang if (ctrl & SDHCI_CTRL_TUNED_CLK) { 2160086fc21Sernest.zhang host->tuning_done = true; 2170086fc21Sernest.zhang return; 2180086fc21Sernest.zhang } 2190086fc21Sernest.zhang pr_warn("%s: HW tuning failed !\n", 2200086fc21Sernest.zhang mmc_hostname(host->mmc)); 2210086fc21Sernest.zhang break; 2220086fc21Sernest.zhang } 2230086fc21Sernest.zhang 2240086fc21Sernest.zhang mdelay(1); 2250086fc21Sernest.zhang } 2260086fc21Sernest.zhang 2270086fc21Sernest.zhang pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", 2280086fc21Sernest.zhang mmc_hostname(host->mmc)); 2290086fc21Sernest.zhang sdhci_reset_tuning(host); 2300086fc21Sernest.zhang } 2310086fc21Sernest.zhang 2327d440617SShirley Her (SC) /* 2337d440617SShirley Her (SC) * This function is used to fix o2 dll shift issue. 2347d440617SShirley Her (SC) * It isn't necessary to detect card present before recovery. 2357d440617SShirley Her (SC) * Firstly, it is used by bht emmc card, which is embedded. 2367d440617SShirley Her (SC) * Second, before call recovery card present will be detected 2377d440617SShirley Her (SC) * outside of the execute tuning function. 2387d440617SShirley Her (SC) */ 2397d440617SShirley Her (SC) static int sdhci_o2_dll_recovery(struct sdhci_host *host) 2407d440617SShirley Her (SC) { 2417d440617SShirley Her (SC) int ret = 0; 2427d440617SShirley Her (SC) u8 scratch_8 = 0; 2437d440617SShirley Her (SC) u32 scratch_32 = 0; 2447d440617SShirley Her (SC) struct sdhci_pci_slot *slot = sdhci_priv(host); 2457d440617SShirley Her (SC) struct sdhci_pci_chip *chip = slot->chip; 2467d440617SShirley Her (SC) struct o2_host *o2_host = sdhci_pci_priv(slot); 2477d440617SShirley Her (SC) 2487d440617SShirley Her (SC) /* UnLock WP */ 2497d440617SShirley Her (SC) pci_read_config_byte(chip->pdev, 2507d440617SShirley Her (SC) O2_SD_LOCK_WP, &scratch_8); 2517d440617SShirley Her (SC) scratch_8 &= 0x7f; 2527d440617SShirley Her (SC) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 2537d440617SShirley Her (SC) while (o2_host->dll_adjust_count < DMDN_SZ && !ret) { 2547d440617SShirley Her (SC) /* Disable clock */ 2557d440617SShirley Her (SC) sdhci_writeb(host, 0, SDHCI_CLOCK_CONTROL); 2567d440617SShirley Her (SC) 2577d440617SShirley Her (SC) /* PLL software reset */ 2587d440617SShirley Her (SC) scratch_32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 2597d440617SShirley Her (SC) scratch_32 |= O2_PLL_SOFT_RESET; 2607d440617SShirley Her (SC) sdhci_writel(host, scratch_32, O2_PLL_DLL_WDT_CONTROL1); 2617d440617SShirley Her (SC) 2627d440617SShirley Her (SC) pci_read_config_dword(chip->pdev, 2637d440617SShirley Her (SC) O2_SD_FUNC_REG4, 2647d440617SShirley Her (SC) &scratch_32); 2657d440617SShirley Her (SC) /* Enable Base Clk setting change */ 2667d440617SShirley Her (SC) scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; 2677d440617SShirley Her (SC) pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG4, scratch_32); 2687d440617SShirley Her (SC) o2_pci_set_baseclk(chip, dmdn_table[o2_host->dll_adjust_count]); 2697d440617SShirley Her (SC) 2707d440617SShirley Her (SC) /* Enable internal clock */ 2717d440617SShirley Her (SC) scratch_8 = SDHCI_CLOCK_INT_EN; 2727d440617SShirley Her (SC) sdhci_writeb(host, scratch_8, SDHCI_CLOCK_CONTROL); 2737d440617SShirley Her (SC) 2747d440617SShirley Her (SC) if (sdhci_o2_get_cd(host->mmc)) { 2757d440617SShirley Her (SC) /* 2767d440617SShirley Her (SC) * need wait at least 5ms for dll status stable, 2777d440617SShirley Her (SC) * after enable internal clock 2787d440617SShirley Her (SC) */ 2797d440617SShirley Her (SC) usleep_range(5000, 6000); 2807d440617SShirley Her (SC) if (sdhci_o2_wait_dll_detect_lock(host)) { 2817d440617SShirley Her (SC) scratch_8 |= SDHCI_CLOCK_CARD_EN; 2827d440617SShirley Her (SC) sdhci_writeb(host, scratch_8, 2837d440617SShirley Her (SC) SDHCI_CLOCK_CONTROL); 2847d440617SShirley Her (SC) ret = 1; 2857d440617SShirley Her (SC) } else { 2867d440617SShirley Her (SC) pr_warn("%s: DLL unlocked when dll_adjust_count is %d.\n", 2877d440617SShirley Her (SC) mmc_hostname(host->mmc), 2887d440617SShirley Her (SC) o2_host->dll_adjust_count); 2897d440617SShirley Her (SC) } 2907d440617SShirley Her (SC) } else { 2917d440617SShirley Her (SC) pr_err("%s: card present detect failed.\n", 2927d440617SShirley Her (SC) mmc_hostname(host->mmc)); 2937d440617SShirley Her (SC) break; 2947d440617SShirley Her (SC) } 2957d440617SShirley Her (SC) 2967d440617SShirley Her (SC) o2_host->dll_adjust_count++; 2977d440617SShirley Her (SC) } 2987d440617SShirley Her (SC) if (!ret && o2_host->dll_adjust_count == DMDN_SZ) 2997d440617SShirley Her (SC) pr_err("%s: DLL adjust over max times\n", 3007d440617SShirley Her (SC) mmc_hostname(host->mmc)); 3017d440617SShirley Her (SC) /* Lock WP */ 3027d440617SShirley Her (SC) pci_read_config_byte(chip->pdev, 3037d440617SShirley Her (SC) O2_SD_LOCK_WP, &scratch_8); 3047d440617SShirley Her (SC) scratch_8 |= 0x80; 3057d440617SShirley Her (SC) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 3067d440617SShirley Her (SC) return ret; 3077d440617SShirley Her (SC) } 3087d440617SShirley Her (SC) 3090086fc21Sernest.zhang static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) 3100086fc21Sernest.zhang { 3110086fc21Sernest.zhang struct sdhci_host *host = mmc_priv(mmc); 3124be33cf1SFred Ai struct sdhci_pci_slot *slot = sdhci_priv(host); 3134be33cf1SFred Ai struct sdhci_pci_chip *chip = slot->chip; 3140086fc21Sernest.zhang int current_bus_width = 0; 3151ad9f880SShirley Her u32 scratch32 = 0; 3161ad9f880SShirley Her u16 scratch = 0; 3174be33cf1SFred Ai u8 scratch_8 = 0; 3184be33cf1SFred Ai u32 reg_val; 3190086fc21Sernest.zhang 3200086fc21Sernest.zhang /* 32150aeccccSChevron Li * This handler implements the hardware tuning that is specific to 3220086fc21Sernest.zhang * this controller. Fall back to the standard method for other TIMING. 3230086fc21Sernest.zhang */ 3247b7d897eSshirley her if ((host->timing != MMC_TIMING_MMC_HS200) && 32550aeccccSChevron Li (host->timing != MMC_TIMING_UHS_SDR104) && 32650aeccccSChevron Li (host->timing != MMC_TIMING_UHS_SDR50)) 3270086fc21Sernest.zhang return sdhci_execute_tuning(mmc, opcode); 3280086fc21Sernest.zhang 329b98e7e8dSChanWoo Lee if (WARN_ON(!mmc_op_tuning(opcode))) 3300086fc21Sernest.zhang return -EINVAL; 3311ad9f880SShirley Her 3321ad9f880SShirley Her /* Force power mode enter L0 */ 3331ad9f880SShirley Her scratch = sdhci_readw(host, O2_SD_MISC_CTRL); 3341ad9f880SShirley Her scratch |= O2_SD_PWR_FORCE_L0; 3351ad9f880SShirley Her sdhci_writew(host, scratch, O2_SD_MISC_CTRL); 3361ad9f880SShirley Her 3374be33cf1SFred Ai /* Stop clk */ 3384be33cf1SFred Ai reg_val = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 3394be33cf1SFred Ai reg_val &= ~SDHCI_CLOCK_CARD_EN; 3404be33cf1SFred Ai sdhci_writew(host, reg_val, SDHCI_CLOCK_CONTROL); 3414be33cf1SFred Ai 342*51dfc614SFred if ((host->timing == MMC_TIMING_MMC_HS200) || 343*51dfc614SFred (host->timing == MMC_TIMING_UHS_SDR104)) { 3444be33cf1SFred Ai /* UnLock WP */ 3454be33cf1SFred Ai pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); 3464be33cf1SFred Ai scratch_8 &= 0x7f; 3474be33cf1SFred Ai pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 3484be33cf1SFred Ai 3494be33cf1SFred Ai /* Set pcr 0x354[16] to choose dll clock, and set the default phase */ 3504be33cf1SFred Ai pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, ®_val); 3514be33cf1SFred Ai reg_val &= ~(O2_SD_SEL_DLL | O2_SD_PHASE_MASK); 3524be33cf1SFred Ai reg_val |= (O2_SD_SEL_DLL | O2_SD_FIX_PHASE); 3534be33cf1SFred Ai pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, reg_val); 3544be33cf1SFred Ai 3554be33cf1SFred Ai /* Lock WP */ 3564be33cf1SFred Ai pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); 3574be33cf1SFred Ai scratch_8 |= 0x80; 3584be33cf1SFred Ai pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 359*51dfc614SFred } 3604be33cf1SFred Ai /* Start clk */ 3614be33cf1SFred Ai reg_val = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 3624be33cf1SFred Ai reg_val |= SDHCI_CLOCK_CARD_EN; 3634be33cf1SFred Ai sdhci_writew(host, reg_val, SDHCI_CLOCK_CONTROL); 3644be33cf1SFred Ai 3651ad9f880SShirley Her /* wait DLL lock, timeout value 5ms */ 3661ad9f880SShirley Her if (readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host, 3671ad9f880SShirley Her scratch32, (scratch32 & O2_DLL_LOCK_STATUS), 1, 5000)) 3681ad9f880SShirley Her pr_warn("%s: DLL can't lock in 5ms after force L0 during tuning.\n", 3691ad9f880SShirley Her mmc_hostname(host->mmc)); 3707d440617SShirley Her (SC) /* 3717d440617SShirley Her (SC) * Judge the tuning reason, whether caused by dll shift 3727d440617SShirley Her (SC) * If cause by dll shift, should call sdhci_o2_dll_recovery 3737d440617SShirley Her (SC) */ 3747d440617SShirley Her (SC) if (!sdhci_o2_wait_dll_detect_lock(host)) 3757d440617SShirley Her (SC) if (!sdhci_o2_dll_recovery(host)) { 3767d440617SShirley Her (SC) pr_err("%s: o2 dll recovery failed\n", 3777d440617SShirley Her (SC) mmc_hostname(host->mmc)); 3787d440617SShirley Her (SC) return -EINVAL; 3797d440617SShirley Her (SC) } 3800086fc21Sernest.zhang /* 3810086fc21Sernest.zhang * o2 sdhci host didn't support 8bit emmc tuning 3820086fc21Sernest.zhang */ 3830086fc21Sernest.zhang if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { 3840086fc21Sernest.zhang current_bus_width = mmc->ios.bus_width; 3850f7b79a4SRaul E Rangel mmc->ios.bus_width = MMC_BUS_WIDTH_4; 3860086fc21Sernest.zhang sdhci_set_bus_width(host, MMC_BUS_WIDTH_4); 3870086fc21Sernest.zhang } 3880086fc21Sernest.zhang 3890086fc21Sernest.zhang sdhci_o2_set_tuning_mode(host); 3900086fc21Sernest.zhang 3910086fc21Sernest.zhang sdhci_start_tuning(host); 3920086fc21Sernest.zhang 3930086fc21Sernest.zhang __sdhci_o2_execute_tuning(host, opcode); 3940086fc21Sernest.zhang 3950086fc21Sernest.zhang sdhci_end_tuning(host); 3960086fc21Sernest.zhang 3970f7b79a4SRaul E Rangel if (current_bus_width == MMC_BUS_WIDTH_8) { 3980f7b79a4SRaul E Rangel mmc->ios.bus_width = MMC_BUS_WIDTH_8; 3990086fc21Sernest.zhang sdhci_set_bus_width(host, current_bus_width); 4000f7b79a4SRaul E Rangel } 4010086fc21Sernest.zhang 4021ad9f880SShirley Her /* Cancel force power mode enter L0 */ 4031ad9f880SShirley Her scratch = sdhci_readw(host, O2_SD_MISC_CTRL); 4041ad9f880SShirley Her scratch &= ~(O2_SD_PWR_FORCE_L0); 4051ad9f880SShirley Her sdhci_writew(host, scratch, O2_SD_MISC_CTRL); 4061ad9f880SShirley Her 4077b7d897eSshirley her sdhci_reset(host, SDHCI_RESET_CMD); 4087b7d897eSshirley her sdhci_reset(host, SDHCI_RESET_DATA); 4097b7d897eSshirley her 4100086fc21Sernest.zhang host->flags &= ~SDHCI_HS400_TUNING; 4110086fc21Sernest.zhang return 0; 4120086fc21Sernest.zhang } 41301acf691SAdam Lee 414706adf6bSPeter Guo static void o2_pci_led_enable(struct sdhci_pci_chip *chip) 415706adf6bSPeter Guo { 416706adf6bSPeter Guo int ret; 417706adf6bSPeter Guo u32 scratch_32; 418706adf6bSPeter Guo 419706adf6bSPeter Guo /* Set led of SD host function enable */ 420706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 421706adf6bSPeter Guo O2_SD_FUNC_REG0, &scratch_32); 422706adf6bSPeter Guo if (ret) 423706adf6bSPeter Guo return; 424706adf6bSPeter Guo 425706adf6bSPeter Guo scratch_32 &= ~O2_SD_FREG0_LEDOFF; 426706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 427706adf6bSPeter Guo O2_SD_FUNC_REG0, scratch_32); 428706adf6bSPeter Guo 429706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 430706adf6bSPeter Guo O2_SD_TEST_REG, &scratch_32); 431706adf6bSPeter Guo if (ret) 432706adf6bSPeter Guo return; 433706adf6bSPeter Guo 434706adf6bSPeter Guo scratch_32 |= O2_SD_LED_ENABLE; 435706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 436706adf6bSPeter Guo O2_SD_TEST_REG, scratch_32); 437706adf6bSPeter Guo } 438706adf6bSPeter Guo 439f0cbd780SBen Hutchings static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) 44001acf691SAdam Lee { 44101acf691SAdam Lee u32 scratch_32; 44201acf691SAdam Lee int ret; 44301acf691SAdam Lee /* Improve write performance for SD3.0 */ 44401acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32); 44501acf691SAdam Lee if (ret) 44601acf691SAdam Lee return; 44701acf691SAdam Lee scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14)); 44801acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32); 44901acf691SAdam Lee 45001acf691SAdam Lee /* Enable Link abnormal reset generating Reset */ 45101acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32); 45201acf691SAdam Lee if (ret) 45301acf691SAdam Lee return; 45401acf691SAdam Lee scratch_32 &= ~((1 << 19) | (1 << 11)); 45501acf691SAdam Lee scratch_32 |= (1 << 10); 45601acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32); 45701acf691SAdam Lee 45801acf691SAdam Lee /* set card power over current protection */ 45901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32); 46001acf691SAdam Lee if (ret) 46101acf691SAdam Lee return; 46201acf691SAdam Lee scratch_32 |= (1 << 4); 46301acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32); 46401acf691SAdam Lee 46501acf691SAdam Lee /* adjust the output delay for SD mode */ 46601acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492); 46701acf691SAdam Lee 46801acf691SAdam Lee /* Set the output voltage setting of Aux 1.2v LDO */ 46901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32); 47001acf691SAdam Lee if (ret) 47101acf691SAdam Lee return; 47201acf691SAdam Lee scratch_32 &= ~(3 << 12); 47301acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32); 47401acf691SAdam Lee 47501acf691SAdam Lee /* Set Max power supply capability of SD host */ 47601acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32); 47701acf691SAdam Lee if (ret) 47801acf691SAdam Lee return; 47901acf691SAdam Lee scratch_32 &= ~(0x01FE); 48001acf691SAdam Lee scratch_32 |= 0x00CC; 48101acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32); 48201acf691SAdam Lee /* Set DLL Tuning Window */ 48301acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 48401acf691SAdam Lee O2_SD_TUNING_CTRL, &scratch_32); 48501acf691SAdam Lee if (ret) 48601acf691SAdam Lee return; 48701acf691SAdam Lee scratch_32 &= ~(0x000000FF); 48801acf691SAdam Lee scratch_32 |= 0x00000066; 48901acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32); 49001acf691SAdam Lee 49101acf691SAdam Lee /* Set UHS2 T_EIDLE */ 49201acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 49301acf691SAdam Lee O2_SD_UHS2_L1_CTRL, &scratch_32); 49401acf691SAdam Lee if (ret) 49501acf691SAdam Lee return; 49601acf691SAdam Lee scratch_32 &= ~(0x000000FC); 49701acf691SAdam Lee scratch_32 |= 0x00000084; 49801acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32); 49901acf691SAdam Lee 50001acf691SAdam Lee /* Set UHS2 Termination */ 50101acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32); 50201acf691SAdam Lee if (ret) 50301acf691SAdam Lee return; 50401acf691SAdam Lee scratch_32 &= ~((1 << 21) | (1 << 30)); 50501acf691SAdam Lee 50601acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); 50701acf691SAdam Lee 50801acf691SAdam Lee /* Set L1 Entrance Timer */ 50901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32); 51001acf691SAdam Lee if (ret) 51101acf691SAdam Lee return; 51201acf691SAdam Lee scratch_32 &= ~(0xf0000000); 51301acf691SAdam Lee scratch_32 |= 0x30000000; 51401acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32); 51501acf691SAdam Lee 51601acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 51701acf691SAdam Lee O2_SD_MISC_CTRL4, &scratch_32); 51801acf691SAdam Lee if (ret) 51901acf691SAdam Lee return; 52001acf691SAdam Lee scratch_32 &= ~(0x000f0000); 52101acf691SAdam Lee scratch_32 |= 0x00080000; 52201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); 52301acf691SAdam Lee } 52401acf691SAdam Lee 52502a3c0bdSernest.zhang static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip, 52602a3c0bdSernest.zhang struct sdhci_host *host) 52702a3c0bdSernest.zhang { 52802a3c0bdSernest.zhang int ret; 52902a3c0bdSernest.zhang 53002a3c0bdSernest.zhang ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI); 53102a3c0bdSernest.zhang if (!ret) { 5320818d197SColin Ian King pr_info("%s: unsupported MSI, use INTx irq\n", 53302a3c0bdSernest.zhang mmc_hostname(host->mmc)); 53402a3c0bdSernest.zhang return; 53502a3c0bdSernest.zhang } 53602a3c0bdSernest.zhang 53702a3c0bdSernest.zhang ret = pci_alloc_irq_vectors(chip->pdev, 1, 1, 53802a3c0bdSernest.zhang PCI_IRQ_MSI | PCI_IRQ_MSIX); 53902a3c0bdSernest.zhang if (ret < 0) { 54002a3c0bdSernest.zhang pr_err("%s: enable PCI MSI failed, err=%d\n", 54102a3c0bdSernest.zhang mmc_hostname(host->mmc), ret); 54202a3c0bdSernest.zhang return; 54302a3c0bdSernest.zhang } 54402a3c0bdSernest.zhang 54502a3c0bdSernest.zhang host->irq = pci_irq_vector(chip->pdev, 0); 54602a3c0bdSernest.zhang } 54702a3c0bdSernest.zhang 54869d91ed1SErnest Zhang(WH) static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk) 54969d91ed1SErnest Zhang(WH) { 55069d91ed1SErnest Zhang(WH) /* Enable internal clock */ 55169d91ed1SErnest Zhang(WH) clk |= SDHCI_CLOCK_INT_EN; 55269d91ed1SErnest Zhang(WH) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 55369d91ed1SErnest Zhang(WH) 5547d440617SShirley Her (SC) sdhci_o2_enable_internal_clock(host); 55569d91ed1SErnest Zhang(WH) if (sdhci_o2_get_cd(host->mmc)) { 55669d91ed1SErnest Zhang(WH) clk |= SDHCI_CLOCK_CARD_EN; 55769d91ed1SErnest Zhang(WH) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 55869d91ed1SErnest Zhang(WH) } 55969d91ed1SErnest Zhang(WH) } 56069d91ed1SErnest Zhang(WH) 561580b946eSZou Wei static void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock) 56269d91ed1SErnest Zhang(WH) { 56369d91ed1SErnest Zhang(WH) u16 clk; 5647b7d897eSshirley her u8 scratch; 5657b7d897eSshirley her u32 scratch_32; 5667b7d897eSshirley her struct sdhci_pci_slot *slot = sdhci_priv(host); 5677b7d897eSshirley her struct sdhci_pci_chip *chip = slot->chip; 56869d91ed1SErnest Zhang(WH) 56969d91ed1SErnest Zhang(WH) host->mmc->actual_clock = 0; 57069d91ed1SErnest Zhang(WH) 57169d91ed1SErnest Zhang(WH) sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 57269d91ed1SErnest Zhang(WH) 57369d91ed1SErnest Zhang(WH) if (clock == 0) 57469d91ed1SErnest Zhang(WH) return; 57569d91ed1SErnest Zhang(WH) 5764be33cf1SFred Ai /* UnLock WP */ 5777b7d897eSshirley her pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); 5787b7d897eSshirley her scratch &= 0x7f; 5797b7d897eSshirley her pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 5807b7d897eSshirley her 5814be33cf1SFred Ai if ((host->timing == MMC_TIMING_UHS_SDR104) && (clock == 200000000)) { 5827b7d897eSshirley her pci_read_config_dword(chip->pdev, O2_SD_PLL_SETTING, &scratch_32); 5837b7d897eSshirley her 5847b7d897eSshirley her if ((scratch_32 & 0xFFFF0000) != 0x2c280000) 5857b7d897eSshirley her o2_pci_set_baseclk(chip, 0x2c280000); 586a7c18e5cSFred Ai } else { 587a7c18e5cSFred Ai pci_read_config_dword(chip->pdev, O2_SD_PLL_SETTING, &scratch_32); 588a7c18e5cSFred Ai 589a7c18e5cSFred Ai if ((scratch_32 & 0xFFFF0000) != 0x25100000) 590a7c18e5cSFred Ai o2_pci_set_baseclk(chip, 0x25100000); 5914be33cf1SFred Ai } 5927b7d897eSshirley her 5934be33cf1SFred Ai pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, &scratch_32); 5944be33cf1SFred Ai scratch_32 &= ~(O2_SD_SEL_DLL | O2_SD_PHASE_MASK); 5954be33cf1SFred Ai pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, scratch_32); 5964be33cf1SFred Ai 5974be33cf1SFred Ai /* Lock WP */ 5987b7d897eSshirley her pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); 5997b7d897eSshirley her scratch |= 0x80; 6007b7d897eSshirley her pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 6017b7d897eSshirley her 60269d91ed1SErnest Zhang(WH) clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 60369d91ed1SErnest Zhang(WH) sdhci_o2_enable_clk(host, clk); 60469d91ed1SErnest Zhang(WH) } 60569d91ed1SErnest Zhang(WH) 606580b946eSZou Wei static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) 60701acf691SAdam Lee { 60801acf691SAdam Lee struct sdhci_pci_chip *chip; 60901acf691SAdam Lee struct sdhci_host *host; 6107d440617SShirley Her (SC) struct o2_host *o2_host = sdhci_pci_priv(slot); 611de23f0b7SRaul E Rangel u32 reg, caps; 61257322d54Sernest.zhang int ret; 61301acf691SAdam Lee 61401acf691SAdam Lee chip = slot->chip; 61501acf691SAdam Lee host = slot->host; 616de23f0b7SRaul E Rangel 6177d440617SShirley Her (SC) o2_host->dll_adjust_count = 0; 618de23f0b7SRaul E Rangel caps = sdhci_readl(host, SDHCI_CAPABILITIES); 619de23f0b7SRaul E Rangel 620de23f0b7SRaul E Rangel /* 621de23f0b7SRaul E Rangel * mmc_select_bus_width() will test the bus to determine the actual bus 622de23f0b7SRaul E Rangel * width. 623de23f0b7SRaul E Rangel */ 624de23f0b7SRaul E Rangel if (caps & SDHCI_CAN_DO_8BIT) 625de23f0b7SRaul E Rangel host->mmc->caps |= MMC_CAP_8_BIT_DATA; 626de23f0b7SRaul E Rangel 62701acf691SAdam Lee switch (chip->pdev->device) { 62801acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS0: 62901acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD0: 63001acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD1: 63101acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS1: 63201acf691SAdam Lee case PCI_DEVICE_ID_O2_FUJIN2: 63301acf691SAdam Lee reg = sdhci_readl(host, O2_SD_VENDOR_SETTING); 63401acf691SAdam Lee if (reg & 0x1) 63501acf691SAdam Lee host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; 63601acf691SAdam Lee 63750aeccccSChevron Li host->quirks2 |= SDHCI_QUIRK2_BROKEN_DDR50; 63850aeccccSChevron Li 63902a3c0bdSernest.zhang sdhci_pci_o2_enable_msi(chip, host); 64002a3c0bdSernest.zhang 64157322d54Sernest.zhang if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) { 64257322d54Sernest.zhang ret = pci_read_config_dword(chip->pdev, 64357322d54Sernest.zhang O2_SD_MISC_SETTING, ®); 64457322d54Sernest.zhang if (ret) 64557322d54Sernest.zhang return -EIO; 64657322d54Sernest.zhang if (reg & (1 << 4)) { 64757322d54Sernest.zhang pr_info("%s: emmc 1.8v flag is set, force 1.8v signaling voltage\n", 64857322d54Sernest.zhang mmc_hostname(host->mmc)); 64957322d54Sernest.zhang host->flags &= ~SDHCI_SIGNALING_330; 65057322d54Sernest.zhang host->flags |= SDHCI_SIGNALING_180; 65157322d54Sernest.zhang host->mmc->caps2 |= MMC_CAP2_NO_SD; 65257322d54Sernest.zhang host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 65369d91ed1SErnest Zhang(WH) pci_write_config_dword(chip->pdev, 65469d91ed1SErnest Zhang(WH) O2_SD_DETECT_SETTING, 3); 65557322d54Sernest.zhang } 65669d91ed1SErnest Zhang(WH) 65769d91ed1SErnest Zhang(WH) slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd; 65857322d54Sernest.zhang } 65957322d54Sernest.zhang 660cdd2b769Sshirley her if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD1) { 661cdd2b769Sshirley her slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd; 662cdd2b769Sshirley her host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 663cdd2b769Sshirley her host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 664cdd2b769Sshirley her } 665cdd2b769Sshirley her 6660086fc21Sernest.zhang host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning; 6670086fc21Sernest.zhang 66801acf691SAdam Lee if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) 66901acf691SAdam Lee break; 67001acf691SAdam Lee /* set dll watch dog timer */ 67101acf691SAdam Lee reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2); 67201acf691SAdam Lee reg |= (1 << 12); 67301acf691SAdam Lee sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2); 67401acf691SAdam Lee 67501acf691SAdam Lee break; 67601acf691SAdam Lee default: 67701acf691SAdam Lee break; 67801acf691SAdam Lee } 67901acf691SAdam Lee 68001acf691SAdam Lee return 0; 68101acf691SAdam Lee } 68201acf691SAdam Lee 683580b946eSZou Wei static int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) 68401acf691SAdam Lee { 68501acf691SAdam Lee int ret; 68601acf691SAdam Lee u8 scratch; 68701acf691SAdam Lee u32 scratch_32; 68801acf691SAdam Lee 68901acf691SAdam Lee switch (chip->pdev->device) { 69001acf691SAdam Lee case PCI_DEVICE_ID_O2_8220: 69101acf691SAdam Lee case PCI_DEVICE_ID_O2_8221: 69201acf691SAdam Lee case PCI_DEVICE_ID_O2_8320: 69301acf691SAdam Lee case PCI_DEVICE_ID_O2_8321: 69401acf691SAdam Lee /* This extra setup is required due to broken ADMA. */ 69501acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 69601acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 69701acf691SAdam Lee if (ret) 69801acf691SAdam Lee return ret; 69901acf691SAdam Lee scratch &= 0x7f; 70001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 70101acf691SAdam Lee 70201acf691SAdam Lee /* Set Multi 3 to VCC3V# */ 70301acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); 70401acf691SAdam Lee 70501acf691SAdam Lee /* Disable CLK_REQ# support after media DET */ 70601acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 70701acf691SAdam Lee O2_SD_CLKREQ, &scratch); 70801acf691SAdam Lee if (ret) 70901acf691SAdam Lee return ret; 71001acf691SAdam Lee scratch |= 0x20; 71101acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); 71201acf691SAdam Lee 71301acf691SAdam Lee /* Choose capabilities, enable SDMA. We have to write 0x01 71401acf691SAdam Lee * to the capabilities register first to unlock it. 71501acf691SAdam Lee */ 71601acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); 71701acf691SAdam Lee if (ret) 71801acf691SAdam Lee return ret; 71901acf691SAdam Lee scratch |= 0x01; 72001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); 72101acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); 72201acf691SAdam Lee 72301acf691SAdam Lee /* Disable ADMA1/2 */ 72401acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); 72501acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); 72601acf691SAdam Lee 72701acf691SAdam Lee /* Disable the infinite transfer mode */ 72801acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 72901acf691SAdam Lee O2_SD_INF_MOD, &scratch); 73001acf691SAdam Lee if (ret) 73101acf691SAdam Lee return ret; 73201acf691SAdam Lee scratch |= 0x08; 73301acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); 73401acf691SAdam Lee 73501acf691SAdam Lee /* Lock WP */ 73601acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 73701acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 73801acf691SAdam Lee if (ret) 73901acf691SAdam Lee return ret; 74001acf691SAdam Lee scratch |= 0x80; 74101acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 74201acf691SAdam Lee break; 74301acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS0: 74401acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS1: 74501acf691SAdam Lee case PCI_DEVICE_ID_O2_FUJIN2: 74601acf691SAdam Lee /* UnLock WP */ 74701acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 74801acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 74901acf691SAdam Lee if (ret) 75001acf691SAdam Lee return ret; 75101acf691SAdam Lee 75201acf691SAdam Lee scratch &= 0x7f; 75301acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 75401acf691SAdam Lee 755706adf6bSPeter Guo /* DevId=8520 subId= 0x11 or 0x12 Type Chip support */ 756706adf6bSPeter Guo if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) { 757706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 758706adf6bSPeter Guo O2_SD_FUNC_REG0, 759706adf6bSPeter Guo &scratch_32); 760d599005aSDinghao Liu if (ret) 761d599005aSDinghao Liu return ret; 762706adf6bSPeter Guo scratch_32 = ((scratch_32 & 0xFF000000) >> 24); 763706adf6bSPeter Guo 764706adf6bSPeter Guo /* Check Whether subId is 0x11 or 0x12 */ 765706adf6bSPeter Guo if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { 7663665ff03Sernest.zhang scratch_32 = 0x25100000; 767706adf6bSPeter Guo 768706adf6bSPeter Guo o2_pci_set_baseclk(chip, scratch_32); 769706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 770706adf6bSPeter Guo O2_SD_FUNC_REG4, 771706adf6bSPeter Guo &scratch_32); 772d599005aSDinghao Liu if (ret) 773d599005aSDinghao Liu return ret; 774706adf6bSPeter Guo 775706adf6bSPeter Guo /* Enable Base Clk setting change */ 776706adf6bSPeter Guo scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; 777706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 778706adf6bSPeter Guo O2_SD_FUNC_REG4, 779706adf6bSPeter Guo scratch_32); 780706adf6bSPeter Guo 781706adf6bSPeter Guo /* Set Tuning Window to 4 */ 782706adf6bSPeter Guo pci_write_config_byte(chip->pdev, 783706adf6bSPeter Guo O2_SD_TUNING_CTRL, 0x44); 784706adf6bSPeter Guo 785706adf6bSPeter Guo break; 786706adf6bSPeter Guo } 787706adf6bSPeter Guo } 788706adf6bSPeter Guo 789706adf6bSPeter Guo /* Enable 8520 led function */ 790706adf6bSPeter Guo o2_pci_led_enable(chip); 791706adf6bSPeter Guo 79201acf691SAdam Lee /* Set timeout CLK */ 79301acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 79401acf691SAdam Lee O2_SD_CLK_SETTING, &scratch_32); 79501acf691SAdam Lee if (ret) 79601acf691SAdam Lee return ret; 79701acf691SAdam Lee 79801acf691SAdam Lee scratch_32 &= ~(0xFF00); 79901acf691SAdam Lee scratch_32 |= 0x07E0C800; 80001acf691SAdam Lee pci_write_config_dword(chip->pdev, 80101acf691SAdam Lee O2_SD_CLK_SETTING, scratch_32); 80201acf691SAdam Lee 80301acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 80401acf691SAdam Lee O2_SD_CLKREQ, &scratch_32); 80501acf691SAdam Lee if (ret) 80601acf691SAdam Lee return ret; 80701acf691SAdam Lee scratch_32 |= 0x3; 80801acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32); 80901acf691SAdam Lee 81001acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 81101acf691SAdam Lee O2_SD_PLL_SETTING, &scratch_32); 81201acf691SAdam Lee if (ret) 81301acf691SAdam Lee return ret; 81401acf691SAdam Lee 81501acf691SAdam Lee scratch_32 &= ~(0x1F3F070E); 81601acf691SAdam Lee scratch_32 |= 0x18270106; 81701acf691SAdam Lee pci_write_config_dword(chip->pdev, 81801acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 81901acf691SAdam Lee 82001acf691SAdam Lee /* Disable UHS1 funciton */ 82101acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 82201acf691SAdam Lee O2_SD_CAP_REG2, &scratch_32); 82301acf691SAdam Lee if (ret) 82401acf691SAdam Lee return ret; 82501acf691SAdam Lee scratch_32 &= ~(0xE0); 82601acf691SAdam Lee pci_write_config_dword(chip->pdev, 82701acf691SAdam Lee O2_SD_CAP_REG2, scratch_32); 82801acf691SAdam Lee 82901acf691SAdam Lee if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) 83001acf691SAdam Lee sdhci_pci_o2_fujin2_pci_init(chip); 83101acf691SAdam Lee 83201acf691SAdam Lee /* Lock WP */ 83301acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 83401acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 83501acf691SAdam Lee if (ret) 83601acf691SAdam Lee return ret; 83701acf691SAdam Lee scratch |= 0x80; 83801acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 83901acf691SAdam Lee break; 84001acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD0: 84101acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD1: 84201acf691SAdam Lee /* UnLock WP */ 84301acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 84401acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 84501acf691SAdam Lee if (ret) 84601acf691SAdam Lee return ret; 84701acf691SAdam Lee 84801acf691SAdam Lee scratch &= 0x7f; 84901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 85001acf691SAdam Lee 85101acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 852706adf6bSPeter Guo O2_SD_PLL_SETTING, &scratch_32); 853d599005aSDinghao Liu if (ret) 854d599005aSDinghao Liu return ret; 85501acf691SAdam Lee 85601acf691SAdam Lee if ((scratch_32 & 0xff000000) == 0x01000000) { 85701acf691SAdam Lee scratch_32 &= 0x0000FFFF; 85801acf691SAdam Lee scratch_32 |= 0x1F340000; 85901acf691SAdam Lee 86001acf691SAdam Lee pci_write_config_dword(chip->pdev, 86101acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 86201acf691SAdam Lee } else { 86301acf691SAdam Lee scratch_32 &= 0x0000FFFF; 8643665ff03Sernest.zhang scratch_32 |= 0x25100000; 86501acf691SAdam Lee 86601acf691SAdam Lee pci_write_config_dword(chip->pdev, 86701acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 86801acf691SAdam Lee 86901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 87001acf691SAdam Lee O2_SD_FUNC_REG4, 87101acf691SAdam Lee &scratch_32); 872d599005aSDinghao Liu if (ret) 873d599005aSDinghao Liu return ret; 87401acf691SAdam Lee scratch_32 |= (1 << 22); 87501acf691SAdam Lee pci_write_config_dword(chip->pdev, 87601acf691SAdam Lee O2_SD_FUNC_REG4, scratch_32); 87701acf691SAdam Lee } 87801acf691SAdam Lee 879706adf6bSPeter Guo /* Set Tuning Windows to 5 */ 880706adf6bSPeter Guo pci_write_config_byte(chip->pdev, 881706adf6bSPeter Guo O2_SD_TUNING_CTRL, 0x55); 882096cc0cdSChevron Li //Adjust 1st and 2nd CD debounce time 883096cc0cdSChevron Li pci_read_config_dword(chip->pdev, O2_SD_MISC_CTRL2, &scratch_32); 884096cc0cdSChevron Li scratch_32 &= 0xFFE7FFFF; 885096cc0cdSChevron Li scratch_32 |= 0x00180000; 886096cc0cdSChevron Li pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL2, scratch_32); 887096cc0cdSChevron Li pci_write_config_dword(chip->pdev, O2_SD_DETECT_SETTING, 1); 88801acf691SAdam Lee /* Lock WP */ 88901acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 89001acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 89101acf691SAdam Lee if (ret) 89201acf691SAdam Lee return ret; 89301acf691SAdam Lee scratch |= 0x80; 89401acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 89501acf691SAdam Lee break; 89601acf691SAdam Lee } 89701acf691SAdam Lee 89801acf691SAdam Lee return 0; 89901acf691SAdam Lee } 90001acf691SAdam Lee 901b7813f0fSAdrian Hunter #ifdef CONFIG_PM_SLEEP 902580b946eSZou Wei static int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) 90301acf691SAdam Lee { 90401acf691SAdam Lee sdhci_pci_o2_probe(chip); 90530cf2803SAdrian Hunter return sdhci_pci_resume_host(chip); 90601acf691SAdam Lee } 907b7813f0fSAdrian Hunter #endif 908328be8beSErnest Zhang(WH) 90969d91ed1SErnest Zhang(WH) static const struct sdhci_ops sdhci_pci_o2_ops = { 91069d91ed1SErnest Zhang(WH) .set_clock = sdhci_pci_o2_set_clock, 91169d91ed1SErnest Zhang(WH) .enable_dma = sdhci_pci_enable_dma, 91269d91ed1SErnest Zhang(WH) .set_bus_width = sdhci_set_bus_width, 91369d91ed1SErnest Zhang(WH) .reset = sdhci_reset, 91469d91ed1SErnest Zhang(WH) .set_uhs_signaling = sdhci_set_uhs_signaling, 91569d91ed1SErnest Zhang(WH) }; 91669d91ed1SErnest Zhang(WH) 917328be8beSErnest Zhang(WH) const struct sdhci_pci_fixes sdhci_o2 = { 918328be8beSErnest Zhang(WH) .probe = sdhci_pci_o2_probe, 919328be8beSErnest Zhang(WH) .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 92049baa01cSDaniel Drake .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, 921328be8beSErnest Zhang(WH) .probe_slot = sdhci_pci_o2_probe_slot, 922328be8beSErnest Zhang(WH) #ifdef CONFIG_PM_SLEEP 923328be8beSErnest Zhang(WH) .resume = sdhci_pci_o2_resume, 924328be8beSErnest Zhang(WH) #endif 92569d91ed1SErnest Zhang(WH) .ops = &sdhci_pci_o2_ops, 9267d440617SShirley Her (SC) .priv_size = sizeof(struct o2_host), 927328be8beSErnest Zhang(WH) }; 928