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> 14*7d440617SShirley Her (SC) #include <linux/iopoll.h> 1501acf691SAdam Lee 1601acf691SAdam Lee #include "sdhci.h" 1701acf691SAdam Lee #include "sdhci-pci.h" 18361eeda0SAdrian Hunter 19361eeda0SAdrian Hunter /* 20361eeda0SAdrian Hunter * O2Micro device registers 21361eeda0SAdrian Hunter */ 22361eeda0SAdrian Hunter 23361eeda0SAdrian Hunter #define O2_SD_MISC_REG5 0x64 24361eeda0SAdrian Hunter #define O2_SD_LD0_CTRL 0x68 25361eeda0SAdrian Hunter #define O2_SD_DEV_CTRL 0x88 26361eeda0SAdrian Hunter #define O2_SD_LOCK_WP 0xD3 27361eeda0SAdrian Hunter #define O2_SD_TEST_REG 0xD4 28361eeda0SAdrian Hunter #define O2_SD_FUNC_REG0 0xDC 29361eeda0SAdrian Hunter #define O2_SD_MULTI_VCC3V 0xEE 30361eeda0SAdrian Hunter #define O2_SD_CLKREQ 0xEC 31361eeda0SAdrian Hunter #define O2_SD_CAPS 0xE0 32361eeda0SAdrian Hunter #define O2_SD_ADMA1 0xE2 33361eeda0SAdrian Hunter #define O2_SD_ADMA2 0xE7 34361eeda0SAdrian Hunter #define O2_SD_INF_MOD 0xF1 35361eeda0SAdrian Hunter #define O2_SD_MISC_CTRL4 0xFC 36361eeda0SAdrian Hunter #define O2_SD_TUNING_CTRL 0x300 37361eeda0SAdrian Hunter #define O2_SD_PLL_SETTING 0x304 3857322d54Sernest.zhang #define O2_SD_MISC_SETTING 0x308 39361eeda0SAdrian Hunter #define O2_SD_CLK_SETTING 0x328 40361eeda0SAdrian Hunter #define O2_SD_CAP_REG2 0x330 41361eeda0SAdrian Hunter #define O2_SD_CAP_REG0 0x334 42361eeda0SAdrian Hunter #define O2_SD_UHS1_CAP_SETTING 0x33C 43361eeda0SAdrian Hunter #define O2_SD_DELAY_CTRL 0x350 44361eeda0SAdrian Hunter #define O2_SD_UHS2_L1_CTRL 0x35C 45361eeda0SAdrian Hunter #define O2_SD_FUNC_REG3 0x3E0 46361eeda0SAdrian Hunter #define O2_SD_FUNC_REG4 0x3E4 47361eeda0SAdrian Hunter #define O2_SD_LED_ENABLE BIT(6) 48361eeda0SAdrian Hunter #define O2_SD_FREG0_LEDOFF BIT(13) 49361eeda0SAdrian Hunter #define O2_SD_FREG4_ENABLE_CLK_SET BIT(22) 50361eeda0SAdrian Hunter 51361eeda0SAdrian Hunter #define O2_SD_VENDOR_SETTING 0x110 52361eeda0SAdrian Hunter #define O2_SD_VENDOR_SETTING2 0x1C8 530086fc21Sernest.zhang #define O2_SD_HW_TUNING_DISABLE BIT(4) 540086fc21Sernest.zhang 559674bab4SShirley Her (SC) #define O2_PLL_DLL_WDT_CONTROL1 0x1CC 5669d91ed1SErnest Zhang(WH) #define O2_PLL_FORCE_ACTIVE BIT(18) 5769d91ed1SErnest Zhang(WH) #define O2_PLL_LOCK_STATUS BIT(14) 5869d91ed1SErnest Zhang(WH) #define O2_PLL_SOFT_RESET BIT(12) 59*7d440617SShirley Her (SC) #define O2_DLL_LOCK_STATUS BIT(11) 6069d91ed1SErnest Zhang(WH) 6169d91ed1SErnest Zhang(WH) #define O2_SD_DETECT_SETTING 0x324 6269d91ed1SErnest Zhang(WH) 63*7d440617SShirley Her (SC) static const u32 dmdn_table[] = {0x2B1C0000, 64*7d440617SShirley Her (SC) 0x2C1A0000, 0x371B0000, 0x35100000}; 65*7d440617SShirley Her (SC) #define DMDN_SZ ARRAY_SIZE(dmdn_table) 66*7d440617SShirley Her (SC) 67*7d440617SShirley Her (SC) struct o2_host { 68*7d440617SShirley Her (SC) u8 dll_adjust_count; 69*7d440617SShirley Her (SC) }; 70*7d440617SShirley Her (SC) 71908fd508SShirley Her (SC) static void sdhci_o2_wait_card_detect_stable(struct sdhci_host *host) 72908fd508SShirley Her (SC) { 73908fd508SShirley Her (SC) ktime_t timeout; 74908fd508SShirley Her (SC) u32 scratch32; 75908fd508SShirley Her (SC) 76908fd508SShirley Her (SC) /* Wait max 50 ms */ 77908fd508SShirley Her (SC) timeout = ktime_add_ms(ktime_get(), 50); 78908fd508SShirley Her (SC) while (1) { 79908fd508SShirley Her (SC) bool timedout = ktime_after(ktime_get(), timeout); 80908fd508SShirley Her (SC) 81908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, SDHCI_PRESENT_STATE); 82908fd508SShirley Her (SC) if ((scratch32 & SDHCI_CARD_PRESENT) >> SDHCI_CARD_PRES_SHIFT 83908fd508SShirley Her (SC) == (scratch32 & SDHCI_CD_LVL) >> SDHCI_CD_LVL_SHIFT) 84908fd508SShirley Her (SC) break; 85908fd508SShirley Her (SC) 86908fd508SShirley Her (SC) if (timedout) { 87908fd508SShirley Her (SC) pr_err("%s: Card Detect debounce never finished.\n", 88908fd508SShirley Her (SC) mmc_hostname(host->mmc)); 89908fd508SShirley Her (SC) sdhci_dumpregs(host); 90908fd508SShirley Her (SC) return; 91908fd508SShirley Her (SC) } 92908fd508SShirley Her (SC) udelay(10); 93908fd508SShirley Her (SC) } 94908fd508SShirley Her (SC) } 95908fd508SShirley Her (SC) 96908fd508SShirley Her (SC) static void sdhci_o2_enable_internal_clock(struct sdhci_host *host) 97908fd508SShirley Her (SC) { 98908fd508SShirley Her (SC) ktime_t timeout; 99908fd508SShirley Her (SC) u16 scratch; 100908fd508SShirley Her (SC) u32 scratch32; 101908fd508SShirley Her (SC) 102908fd508SShirley Her (SC) /* PLL software reset */ 103908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 104908fd508SShirley Her (SC) scratch32 |= O2_PLL_SOFT_RESET; 105908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 106908fd508SShirley Her (SC) udelay(1); 107908fd508SShirley Her (SC) scratch32 &= ~(O2_PLL_SOFT_RESET); 108908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 109908fd508SShirley Her (SC) 110908fd508SShirley Her (SC) /* PLL force active */ 111908fd508SShirley Her (SC) scratch32 |= O2_PLL_FORCE_ACTIVE; 112908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 113908fd508SShirley Her (SC) 114908fd508SShirley Her (SC) /* Wait max 20 ms */ 115908fd508SShirley Her (SC) timeout = ktime_add_ms(ktime_get(), 20); 116908fd508SShirley Her (SC) while (1) { 117908fd508SShirley Her (SC) bool timedout = ktime_after(ktime_get(), timeout); 118908fd508SShirley Her (SC) 119908fd508SShirley Her (SC) scratch = sdhci_readw(host, O2_PLL_DLL_WDT_CONTROL1); 120908fd508SShirley Her (SC) if (scratch & O2_PLL_LOCK_STATUS) 121908fd508SShirley Her (SC) break; 122908fd508SShirley Her (SC) if (timedout) { 123908fd508SShirley Her (SC) pr_err("%s: Internal clock never stabilised.\n", 124908fd508SShirley Her (SC) mmc_hostname(host->mmc)); 125908fd508SShirley Her (SC) sdhci_dumpregs(host); 126908fd508SShirley Her (SC) goto out; 127908fd508SShirley Her (SC) } 128908fd508SShirley Her (SC) udelay(10); 129908fd508SShirley Her (SC) } 130908fd508SShirley Her (SC) 131908fd508SShirley Her (SC) /* Wait for card detect finish */ 132908fd508SShirley Her (SC) udelay(1); 133908fd508SShirley Her (SC) sdhci_o2_wait_card_detect_stable(host); 134908fd508SShirley Her (SC) 135908fd508SShirley Her (SC) out: 136908fd508SShirley Her (SC) /* Cancel PLL force active */ 137908fd508SShirley Her (SC) scratch32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 138908fd508SShirley Her (SC) scratch32 &= ~O2_PLL_FORCE_ACTIVE; 139908fd508SShirley Her (SC) sdhci_writel(host, scratch32, O2_PLL_DLL_WDT_CONTROL1); 140908fd508SShirley Her (SC) } 141908fd508SShirley Her (SC) 142908fd508SShirley Her (SC) static int sdhci_o2_get_cd(struct mmc_host *mmc) 143908fd508SShirley Her (SC) { 144908fd508SShirley Her (SC) struct sdhci_host *host = mmc_priv(mmc); 145908fd508SShirley Her (SC) 146*7d440617SShirley Her (SC) if (!(sdhci_readw(host, O2_PLL_DLL_WDT_CONTROL1) & O2_PLL_LOCK_STATUS)) 147908fd508SShirley Her (SC) sdhci_o2_enable_internal_clock(host); 148908fd508SShirley Her (SC) 149908fd508SShirley Her (SC) return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); 150908fd508SShirley Her (SC) } 151908fd508SShirley Her (SC) 152908fd508SShirley Her (SC) static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) 153908fd508SShirley Her (SC) { 154908fd508SShirley Her (SC) u32 scratch_32; 155908fd508SShirley Her (SC) 156908fd508SShirley Her (SC) pci_read_config_dword(chip->pdev, 157908fd508SShirley Her (SC) O2_SD_PLL_SETTING, &scratch_32); 158908fd508SShirley Her (SC) 159908fd508SShirley Her (SC) scratch_32 &= 0x0000FFFF; 160908fd508SShirley Her (SC) scratch_32 |= value; 161908fd508SShirley Her (SC) 162908fd508SShirley Her (SC) pci_write_config_dword(chip->pdev, 163908fd508SShirley Her (SC) O2_SD_PLL_SETTING, scratch_32); 164908fd508SShirley Her (SC) } 165908fd508SShirley Her (SC) 166*7d440617SShirley Her (SC) static u32 sdhci_o2_pll_dll_wdt_control(struct sdhci_host *host) 167*7d440617SShirley Her (SC) { 168*7d440617SShirley Her (SC) return sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 169*7d440617SShirley Her (SC) } 170*7d440617SShirley Her (SC) 171*7d440617SShirley Her (SC) /* 172*7d440617SShirley Her (SC) * This function is used to detect dll lock status. 173*7d440617SShirley Her (SC) * Since the dll lock status bit will toggle randomly 174*7d440617SShirley Her (SC) * with very short interval which needs to be polled 175*7d440617SShirley Her (SC) * as fast as possible. Set sleep_us as 1 microsecond. 176*7d440617SShirley Her (SC) */ 177*7d440617SShirley Her (SC) static int sdhci_o2_wait_dll_detect_lock(struct sdhci_host *host) 178*7d440617SShirley Her (SC) { 179*7d440617SShirley Her (SC) u32 scratch32 = 0; 180*7d440617SShirley Her (SC) 181*7d440617SShirley Her (SC) return readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host, 182*7d440617SShirley Her (SC) scratch32, !(scratch32 & O2_DLL_LOCK_STATUS), 1, 1000000); 183*7d440617SShirley Her (SC) } 184*7d440617SShirley Her (SC) 1850086fc21Sernest.zhang static void sdhci_o2_set_tuning_mode(struct sdhci_host *host) 1860086fc21Sernest.zhang { 1870086fc21Sernest.zhang u16 reg; 1880086fc21Sernest.zhang 1890086fc21Sernest.zhang /* enable hardware tuning */ 1900086fc21Sernest.zhang reg = sdhci_readw(host, O2_SD_VENDOR_SETTING); 1910086fc21Sernest.zhang reg &= ~O2_SD_HW_TUNING_DISABLE; 1920086fc21Sernest.zhang sdhci_writew(host, reg, O2_SD_VENDOR_SETTING); 1930086fc21Sernest.zhang } 1940086fc21Sernest.zhang 1950086fc21Sernest.zhang static void __sdhci_o2_execute_tuning(struct sdhci_host *host, u32 opcode) 1960086fc21Sernest.zhang { 1970086fc21Sernest.zhang int i; 1980086fc21Sernest.zhang 1990086fc21Sernest.zhang sdhci_send_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); 2000086fc21Sernest.zhang 2010086fc21Sernest.zhang for (i = 0; i < 150; i++) { 2020086fc21Sernest.zhang u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 2030086fc21Sernest.zhang 2040086fc21Sernest.zhang if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { 2050086fc21Sernest.zhang if (ctrl & SDHCI_CTRL_TUNED_CLK) { 2060086fc21Sernest.zhang host->tuning_done = true; 2070086fc21Sernest.zhang return; 2080086fc21Sernest.zhang } 2090086fc21Sernest.zhang pr_warn("%s: HW tuning failed !\n", 2100086fc21Sernest.zhang mmc_hostname(host->mmc)); 2110086fc21Sernest.zhang break; 2120086fc21Sernest.zhang } 2130086fc21Sernest.zhang 2140086fc21Sernest.zhang mdelay(1); 2150086fc21Sernest.zhang } 2160086fc21Sernest.zhang 2170086fc21Sernest.zhang pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", 2180086fc21Sernest.zhang mmc_hostname(host->mmc)); 2190086fc21Sernest.zhang sdhci_reset_tuning(host); 2200086fc21Sernest.zhang } 2210086fc21Sernest.zhang 222*7d440617SShirley Her (SC) /* 223*7d440617SShirley Her (SC) * This function is used to fix o2 dll shift issue. 224*7d440617SShirley Her (SC) * It isn't necessary to detect card present before recovery. 225*7d440617SShirley Her (SC) * Firstly, it is used by bht emmc card, which is embedded. 226*7d440617SShirley Her (SC) * Second, before call recovery card present will be detected 227*7d440617SShirley Her (SC) * outside of the execute tuning function. 228*7d440617SShirley Her (SC) */ 229*7d440617SShirley Her (SC) static int sdhci_o2_dll_recovery(struct sdhci_host *host) 230*7d440617SShirley Her (SC) { 231*7d440617SShirley Her (SC) int ret = 0; 232*7d440617SShirley Her (SC) u8 scratch_8 = 0; 233*7d440617SShirley Her (SC) u32 scratch_32 = 0; 234*7d440617SShirley Her (SC) struct sdhci_pci_slot *slot = sdhci_priv(host); 235*7d440617SShirley Her (SC) struct sdhci_pci_chip *chip = slot->chip; 236*7d440617SShirley Her (SC) struct o2_host *o2_host = sdhci_pci_priv(slot); 237*7d440617SShirley Her (SC) 238*7d440617SShirley Her (SC) /* UnLock WP */ 239*7d440617SShirley Her (SC) pci_read_config_byte(chip->pdev, 240*7d440617SShirley Her (SC) O2_SD_LOCK_WP, &scratch_8); 241*7d440617SShirley Her (SC) scratch_8 &= 0x7f; 242*7d440617SShirley Her (SC) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 243*7d440617SShirley Her (SC) while (o2_host->dll_adjust_count < DMDN_SZ && !ret) { 244*7d440617SShirley Her (SC) /* Disable clock */ 245*7d440617SShirley Her (SC) sdhci_writeb(host, 0, SDHCI_CLOCK_CONTROL); 246*7d440617SShirley Her (SC) 247*7d440617SShirley Her (SC) /* PLL software reset */ 248*7d440617SShirley Her (SC) scratch_32 = sdhci_readl(host, O2_PLL_DLL_WDT_CONTROL1); 249*7d440617SShirley Her (SC) scratch_32 |= O2_PLL_SOFT_RESET; 250*7d440617SShirley Her (SC) sdhci_writel(host, scratch_32, O2_PLL_DLL_WDT_CONTROL1); 251*7d440617SShirley Her (SC) 252*7d440617SShirley Her (SC) pci_read_config_dword(chip->pdev, 253*7d440617SShirley Her (SC) O2_SD_FUNC_REG4, 254*7d440617SShirley Her (SC) &scratch_32); 255*7d440617SShirley Her (SC) /* Enable Base Clk setting change */ 256*7d440617SShirley Her (SC) scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; 257*7d440617SShirley Her (SC) pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG4, scratch_32); 258*7d440617SShirley Her (SC) o2_pci_set_baseclk(chip, dmdn_table[o2_host->dll_adjust_count]); 259*7d440617SShirley Her (SC) 260*7d440617SShirley Her (SC) /* Enable internal clock */ 261*7d440617SShirley Her (SC) scratch_8 = SDHCI_CLOCK_INT_EN; 262*7d440617SShirley Her (SC) sdhci_writeb(host, scratch_8, SDHCI_CLOCK_CONTROL); 263*7d440617SShirley Her (SC) 264*7d440617SShirley Her (SC) if (sdhci_o2_get_cd(host->mmc)) { 265*7d440617SShirley Her (SC) /* 266*7d440617SShirley Her (SC) * need wait at least 5ms for dll status stable, 267*7d440617SShirley Her (SC) * after enable internal clock 268*7d440617SShirley Her (SC) */ 269*7d440617SShirley Her (SC) usleep_range(5000, 6000); 270*7d440617SShirley Her (SC) if (sdhci_o2_wait_dll_detect_lock(host)) { 271*7d440617SShirley Her (SC) scratch_8 |= SDHCI_CLOCK_CARD_EN; 272*7d440617SShirley Her (SC) sdhci_writeb(host, scratch_8, 273*7d440617SShirley Her (SC) SDHCI_CLOCK_CONTROL); 274*7d440617SShirley Her (SC) ret = 1; 275*7d440617SShirley Her (SC) } else { 276*7d440617SShirley Her (SC) pr_warn("%s: DLL unlocked when dll_adjust_count is %d.\n", 277*7d440617SShirley Her (SC) mmc_hostname(host->mmc), 278*7d440617SShirley Her (SC) o2_host->dll_adjust_count); 279*7d440617SShirley Her (SC) } 280*7d440617SShirley Her (SC) } else { 281*7d440617SShirley Her (SC) pr_err("%s: card present detect failed.\n", 282*7d440617SShirley Her (SC) mmc_hostname(host->mmc)); 283*7d440617SShirley Her (SC) break; 284*7d440617SShirley Her (SC) } 285*7d440617SShirley Her (SC) 286*7d440617SShirley Her (SC) o2_host->dll_adjust_count++; 287*7d440617SShirley Her (SC) } 288*7d440617SShirley Her (SC) if (!ret && o2_host->dll_adjust_count == DMDN_SZ) 289*7d440617SShirley Her (SC) pr_err("%s: DLL adjust over max times\n", 290*7d440617SShirley Her (SC) mmc_hostname(host->mmc)); 291*7d440617SShirley Her (SC) /* Lock WP */ 292*7d440617SShirley Her (SC) pci_read_config_byte(chip->pdev, 293*7d440617SShirley Her (SC) O2_SD_LOCK_WP, &scratch_8); 294*7d440617SShirley Her (SC) scratch_8 |= 0x80; 295*7d440617SShirley Her (SC) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); 296*7d440617SShirley Her (SC) return ret; 297*7d440617SShirley Her (SC) } 298*7d440617SShirley Her (SC) 2990086fc21Sernest.zhang static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) 3000086fc21Sernest.zhang { 3010086fc21Sernest.zhang struct sdhci_host *host = mmc_priv(mmc); 3020086fc21Sernest.zhang int current_bus_width = 0; 3030086fc21Sernest.zhang 3040086fc21Sernest.zhang /* 3050086fc21Sernest.zhang * This handler only implements the eMMC tuning that is specific to 3060086fc21Sernest.zhang * this controller. Fall back to the standard method for other TIMING. 3070086fc21Sernest.zhang */ 3080086fc21Sernest.zhang if (host->timing != MMC_TIMING_MMC_HS200) 3090086fc21Sernest.zhang return sdhci_execute_tuning(mmc, opcode); 3100086fc21Sernest.zhang 3110086fc21Sernest.zhang if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200)) 3120086fc21Sernest.zhang return -EINVAL; 313*7d440617SShirley Her (SC) /* 314*7d440617SShirley Her (SC) * Judge the tuning reason, whether caused by dll shift 315*7d440617SShirley Her (SC) * If cause by dll shift, should call sdhci_o2_dll_recovery 316*7d440617SShirley Her (SC) */ 317*7d440617SShirley Her (SC) if (!sdhci_o2_wait_dll_detect_lock(host)) 318*7d440617SShirley Her (SC) if (!sdhci_o2_dll_recovery(host)) { 319*7d440617SShirley Her (SC) pr_err("%s: o2 dll recovery failed\n", 320*7d440617SShirley Her (SC) mmc_hostname(host->mmc)); 321*7d440617SShirley Her (SC) return -EINVAL; 322*7d440617SShirley Her (SC) } 3230086fc21Sernest.zhang /* 3240086fc21Sernest.zhang * o2 sdhci host didn't support 8bit emmc tuning 3250086fc21Sernest.zhang */ 3260086fc21Sernest.zhang if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { 3270086fc21Sernest.zhang current_bus_width = mmc->ios.bus_width; 3280f7b79a4SRaul E Rangel mmc->ios.bus_width = MMC_BUS_WIDTH_4; 3290086fc21Sernest.zhang sdhci_set_bus_width(host, MMC_BUS_WIDTH_4); 3300086fc21Sernest.zhang } 3310086fc21Sernest.zhang 3320086fc21Sernest.zhang sdhci_o2_set_tuning_mode(host); 3330086fc21Sernest.zhang 3340086fc21Sernest.zhang sdhci_start_tuning(host); 3350086fc21Sernest.zhang 3360086fc21Sernest.zhang __sdhci_o2_execute_tuning(host, opcode); 3370086fc21Sernest.zhang 3380086fc21Sernest.zhang sdhci_end_tuning(host); 3390086fc21Sernest.zhang 3400f7b79a4SRaul E Rangel if (current_bus_width == MMC_BUS_WIDTH_8) { 3410f7b79a4SRaul E Rangel mmc->ios.bus_width = MMC_BUS_WIDTH_8; 3420086fc21Sernest.zhang sdhci_set_bus_width(host, current_bus_width); 3430f7b79a4SRaul E Rangel } 3440086fc21Sernest.zhang 3450086fc21Sernest.zhang host->flags &= ~SDHCI_HS400_TUNING; 3460086fc21Sernest.zhang return 0; 3470086fc21Sernest.zhang } 34801acf691SAdam Lee 349706adf6bSPeter Guo static void o2_pci_led_enable(struct sdhci_pci_chip *chip) 350706adf6bSPeter Guo { 351706adf6bSPeter Guo int ret; 352706adf6bSPeter Guo u32 scratch_32; 353706adf6bSPeter Guo 354706adf6bSPeter Guo /* Set led of SD host function enable */ 355706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 356706adf6bSPeter Guo O2_SD_FUNC_REG0, &scratch_32); 357706adf6bSPeter Guo if (ret) 358706adf6bSPeter Guo return; 359706adf6bSPeter Guo 360706adf6bSPeter Guo scratch_32 &= ~O2_SD_FREG0_LEDOFF; 361706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 362706adf6bSPeter Guo O2_SD_FUNC_REG0, scratch_32); 363706adf6bSPeter Guo 364706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 365706adf6bSPeter Guo O2_SD_TEST_REG, &scratch_32); 366706adf6bSPeter Guo if (ret) 367706adf6bSPeter Guo return; 368706adf6bSPeter Guo 369706adf6bSPeter Guo scratch_32 |= O2_SD_LED_ENABLE; 370706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 371706adf6bSPeter Guo O2_SD_TEST_REG, scratch_32); 372706adf6bSPeter Guo 373706adf6bSPeter Guo } 374706adf6bSPeter Guo 375f0cbd780SBen Hutchings static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) 37601acf691SAdam Lee { 37701acf691SAdam Lee u32 scratch_32; 37801acf691SAdam Lee int ret; 37901acf691SAdam Lee /* Improve write performance for SD3.0 */ 38001acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32); 38101acf691SAdam Lee if (ret) 38201acf691SAdam Lee return; 38301acf691SAdam Lee scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14)); 38401acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32); 38501acf691SAdam Lee 38601acf691SAdam Lee /* Enable Link abnormal reset generating Reset */ 38701acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32); 38801acf691SAdam Lee if (ret) 38901acf691SAdam Lee return; 39001acf691SAdam Lee scratch_32 &= ~((1 << 19) | (1 << 11)); 39101acf691SAdam Lee scratch_32 |= (1 << 10); 39201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32); 39301acf691SAdam Lee 39401acf691SAdam Lee /* set card power over current protection */ 39501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32); 39601acf691SAdam Lee if (ret) 39701acf691SAdam Lee return; 39801acf691SAdam Lee scratch_32 |= (1 << 4); 39901acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32); 40001acf691SAdam Lee 40101acf691SAdam Lee /* adjust the output delay for SD mode */ 40201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492); 40301acf691SAdam Lee 40401acf691SAdam Lee /* Set the output voltage setting of Aux 1.2v LDO */ 40501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32); 40601acf691SAdam Lee if (ret) 40701acf691SAdam Lee return; 40801acf691SAdam Lee scratch_32 &= ~(3 << 12); 40901acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32); 41001acf691SAdam Lee 41101acf691SAdam Lee /* Set Max power supply capability of SD host */ 41201acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32); 41301acf691SAdam Lee if (ret) 41401acf691SAdam Lee return; 41501acf691SAdam Lee scratch_32 &= ~(0x01FE); 41601acf691SAdam Lee scratch_32 |= 0x00CC; 41701acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32); 41801acf691SAdam Lee /* Set DLL Tuning Window */ 41901acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 42001acf691SAdam Lee O2_SD_TUNING_CTRL, &scratch_32); 42101acf691SAdam Lee if (ret) 42201acf691SAdam Lee return; 42301acf691SAdam Lee scratch_32 &= ~(0x000000FF); 42401acf691SAdam Lee scratch_32 |= 0x00000066; 42501acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32); 42601acf691SAdam Lee 42701acf691SAdam Lee /* Set UHS2 T_EIDLE */ 42801acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 42901acf691SAdam Lee O2_SD_UHS2_L1_CTRL, &scratch_32); 43001acf691SAdam Lee if (ret) 43101acf691SAdam Lee return; 43201acf691SAdam Lee scratch_32 &= ~(0x000000FC); 43301acf691SAdam Lee scratch_32 |= 0x00000084; 43401acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32); 43501acf691SAdam Lee 43601acf691SAdam Lee /* Set UHS2 Termination */ 43701acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32); 43801acf691SAdam Lee if (ret) 43901acf691SAdam Lee return; 44001acf691SAdam Lee scratch_32 &= ~((1 << 21) | (1 << 30)); 44101acf691SAdam Lee 44201acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); 44301acf691SAdam Lee 44401acf691SAdam Lee /* Set L1 Entrance Timer */ 44501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32); 44601acf691SAdam Lee if (ret) 44701acf691SAdam Lee return; 44801acf691SAdam Lee scratch_32 &= ~(0xf0000000); 44901acf691SAdam Lee scratch_32 |= 0x30000000; 45001acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32); 45101acf691SAdam Lee 45201acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 45301acf691SAdam Lee O2_SD_MISC_CTRL4, &scratch_32); 45401acf691SAdam Lee if (ret) 45501acf691SAdam Lee return; 45601acf691SAdam Lee scratch_32 &= ~(0x000f0000); 45701acf691SAdam Lee scratch_32 |= 0x00080000; 45801acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); 45901acf691SAdam Lee } 46001acf691SAdam Lee 46102a3c0bdSernest.zhang static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip, 46202a3c0bdSernest.zhang struct sdhci_host *host) 46302a3c0bdSernest.zhang { 46402a3c0bdSernest.zhang int ret; 46502a3c0bdSernest.zhang 46602a3c0bdSernest.zhang ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI); 46702a3c0bdSernest.zhang if (!ret) { 46802a3c0bdSernest.zhang pr_info("%s: unsupport msi, use INTx irq\n", 46902a3c0bdSernest.zhang mmc_hostname(host->mmc)); 47002a3c0bdSernest.zhang return; 47102a3c0bdSernest.zhang } 47202a3c0bdSernest.zhang 47302a3c0bdSernest.zhang ret = pci_alloc_irq_vectors(chip->pdev, 1, 1, 47402a3c0bdSernest.zhang PCI_IRQ_MSI | PCI_IRQ_MSIX); 47502a3c0bdSernest.zhang if (ret < 0) { 47602a3c0bdSernest.zhang pr_err("%s: enable PCI MSI failed, err=%d\n", 47702a3c0bdSernest.zhang mmc_hostname(host->mmc), ret); 47802a3c0bdSernest.zhang return; 47902a3c0bdSernest.zhang } 48002a3c0bdSernest.zhang 48102a3c0bdSernest.zhang host->irq = pci_irq_vector(chip->pdev, 0); 48202a3c0bdSernest.zhang } 48302a3c0bdSernest.zhang 48469d91ed1SErnest Zhang(WH) static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk) 48569d91ed1SErnest Zhang(WH) { 48669d91ed1SErnest Zhang(WH) /* Enable internal clock */ 48769d91ed1SErnest Zhang(WH) clk |= SDHCI_CLOCK_INT_EN; 48869d91ed1SErnest Zhang(WH) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 48969d91ed1SErnest Zhang(WH) 490*7d440617SShirley Her (SC) sdhci_o2_enable_internal_clock(host); 49169d91ed1SErnest Zhang(WH) if (sdhci_o2_get_cd(host->mmc)) { 49269d91ed1SErnest Zhang(WH) clk |= SDHCI_CLOCK_CARD_EN; 49369d91ed1SErnest Zhang(WH) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 49469d91ed1SErnest Zhang(WH) } 49569d91ed1SErnest Zhang(WH) } 49669d91ed1SErnest Zhang(WH) 49769d91ed1SErnest Zhang(WH) void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock) 49869d91ed1SErnest Zhang(WH) { 49969d91ed1SErnest Zhang(WH) u16 clk; 50069d91ed1SErnest Zhang(WH) 50169d91ed1SErnest Zhang(WH) host->mmc->actual_clock = 0; 50269d91ed1SErnest Zhang(WH) 50369d91ed1SErnest Zhang(WH) sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 50469d91ed1SErnest Zhang(WH) 50569d91ed1SErnest Zhang(WH) if (clock == 0) 50669d91ed1SErnest Zhang(WH) return; 50769d91ed1SErnest Zhang(WH) 50869d91ed1SErnest Zhang(WH) clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 50969d91ed1SErnest Zhang(WH) sdhci_o2_enable_clk(host, clk); 51069d91ed1SErnest Zhang(WH) } 51169d91ed1SErnest Zhang(WH) 51201acf691SAdam Lee int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) 51301acf691SAdam Lee { 51401acf691SAdam Lee struct sdhci_pci_chip *chip; 51501acf691SAdam Lee struct sdhci_host *host; 516*7d440617SShirley Her (SC) struct o2_host *o2_host = sdhci_pci_priv(slot); 517de23f0b7SRaul E Rangel u32 reg, caps; 51857322d54Sernest.zhang int ret; 51901acf691SAdam Lee 52001acf691SAdam Lee chip = slot->chip; 52101acf691SAdam Lee host = slot->host; 522de23f0b7SRaul E Rangel 523*7d440617SShirley Her (SC) o2_host->dll_adjust_count = 0; 524de23f0b7SRaul E Rangel caps = sdhci_readl(host, SDHCI_CAPABILITIES); 525de23f0b7SRaul E Rangel 526de23f0b7SRaul E Rangel /* 527de23f0b7SRaul E Rangel * mmc_select_bus_width() will test the bus to determine the actual bus 528de23f0b7SRaul E Rangel * width. 529de23f0b7SRaul E Rangel */ 530de23f0b7SRaul E Rangel if (caps & SDHCI_CAN_DO_8BIT) 531de23f0b7SRaul E Rangel host->mmc->caps |= MMC_CAP_8_BIT_DATA; 532de23f0b7SRaul E Rangel 53301acf691SAdam Lee switch (chip->pdev->device) { 53401acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS0: 53501acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD0: 53601acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD1: 53701acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS1: 53801acf691SAdam Lee case PCI_DEVICE_ID_O2_FUJIN2: 53901acf691SAdam Lee reg = sdhci_readl(host, O2_SD_VENDOR_SETTING); 54001acf691SAdam Lee if (reg & 0x1) 54101acf691SAdam Lee host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; 54201acf691SAdam Lee 54302a3c0bdSernest.zhang sdhci_pci_o2_enable_msi(chip, host); 54402a3c0bdSernest.zhang 54557322d54Sernest.zhang if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) { 54657322d54Sernest.zhang ret = pci_read_config_dword(chip->pdev, 54757322d54Sernest.zhang O2_SD_MISC_SETTING, ®); 54857322d54Sernest.zhang if (ret) 54957322d54Sernest.zhang return -EIO; 55057322d54Sernest.zhang if (reg & (1 << 4)) { 55157322d54Sernest.zhang pr_info("%s: emmc 1.8v flag is set, force 1.8v signaling voltage\n", 55257322d54Sernest.zhang mmc_hostname(host->mmc)); 55357322d54Sernest.zhang host->flags &= ~SDHCI_SIGNALING_330; 55457322d54Sernest.zhang host->flags |= SDHCI_SIGNALING_180; 555414126f9SErnest Zhang(WH) host->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; 55657322d54Sernest.zhang host->mmc->caps2 |= MMC_CAP2_NO_SD; 55757322d54Sernest.zhang host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 55869d91ed1SErnest Zhang(WH) pci_write_config_dword(chip->pdev, 55969d91ed1SErnest Zhang(WH) O2_SD_DETECT_SETTING, 3); 56057322d54Sernest.zhang } 56169d91ed1SErnest Zhang(WH) 56269d91ed1SErnest Zhang(WH) slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd; 56357322d54Sernest.zhang } 56457322d54Sernest.zhang 5650086fc21Sernest.zhang host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning; 5660086fc21Sernest.zhang 56701acf691SAdam Lee if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) 56801acf691SAdam Lee break; 56901acf691SAdam Lee /* set dll watch dog timer */ 57001acf691SAdam Lee reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2); 57101acf691SAdam Lee reg |= (1 << 12); 57201acf691SAdam Lee sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2); 57301acf691SAdam Lee 57401acf691SAdam Lee break; 57501acf691SAdam Lee default: 57601acf691SAdam Lee break; 57701acf691SAdam Lee } 57801acf691SAdam Lee 57901acf691SAdam Lee return 0; 58001acf691SAdam Lee } 58101acf691SAdam Lee 58201acf691SAdam Lee int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) 58301acf691SAdam Lee { 58401acf691SAdam Lee int ret; 58501acf691SAdam Lee u8 scratch; 58601acf691SAdam Lee u32 scratch_32; 58701acf691SAdam Lee 58801acf691SAdam Lee switch (chip->pdev->device) { 58901acf691SAdam Lee case PCI_DEVICE_ID_O2_8220: 59001acf691SAdam Lee case PCI_DEVICE_ID_O2_8221: 59101acf691SAdam Lee case PCI_DEVICE_ID_O2_8320: 59201acf691SAdam Lee case PCI_DEVICE_ID_O2_8321: 59301acf691SAdam Lee /* This extra setup is required due to broken ADMA. */ 59401acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 59501acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 59601acf691SAdam Lee if (ret) 59701acf691SAdam Lee return ret; 59801acf691SAdam Lee scratch &= 0x7f; 59901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 60001acf691SAdam Lee 60101acf691SAdam Lee /* Set Multi 3 to VCC3V# */ 60201acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); 60301acf691SAdam Lee 60401acf691SAdam Lee /* Disable CLK_REQ# support after media DET */ 60501acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 60601acf691SAdam Lee O2_SD_CLKREQ, &scratch); 60701acf691SAdam Lee if (ret) 60801acf691SAdam Lee return ret; 60901acf691SAdam Lee scratch |= 0x20; 61001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); 61101acf691SAdam Lee 61201acf691SAdam Lee /* Choose capabilities, enable SDMA. We have to write 0x01 61301acf691SAdam Lee * to the capabilities register first to unlock it. 61401acf691SAdam Lee */ 61501acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); 61601acf691SAdam Lee if (ret) 61701acf691SAdam Lee return ret; 61801acf691SAdam Lee scratch |= 0x01; 61901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); 62001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); 62101acf691SAdam Lee 62201acf691SAdam Lee /* Disable ADMA1/2 */ 62301acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); 62401acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); 62501acf691SAdam Lee 62601acf691SAdam Lee /* Disable the infinite transfer mode */ 62701acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 62801acf691SAdam Lee O2_SD_INF_MOD, &scratch); 62901acf691SAdam Lee if (ret) 63001acf691SAdam Lee return ret; 63101acf691SAdam Lee scratch |= 0x08; 63201acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); 63301acf691SAdam Lee 63401acf691SAdam Lee /* Lock WP */ 63501acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 63601acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 63701acf691SAdam Lee if (ret) 63801acf691SAdam Lee return ret; 63901acf691SAdam Lee scratch |= 0x80; 64001acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 64101acf691SAdam Lee break; 64201acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS0: 64301acf691SAdam Lee case PCI_DEVICE_ID_O2_SDS1: 64401acf691SAdam Lee case PCI_DEVICE_ID_O2_FUJIN2: 64501acf691SAdam Lee /* UnLock WP */ 64601acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 64701acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 64801acf691SAdam Lee if (ret) 64901acf691SAdam Lee return ret; 65001acf691SAdam Lee 65101acf691SAdam Lee scratch &= 0x7f; 65201acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 65301acf691SAdam Lee 654706adf6bSPeter Guo /* DevId=8520 subId= 0x11 or 0x12 Type Chip support */ 655706adf6bSPeter Guo if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) { 656706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 657706adf6bSPeter Guo O2_SD_FUNC_REG0, 658706adf6bSPeter Guo &scratch_32); 659706adf6bSPeter Guo scratch_32 = ((scratch_32 & 0xFF000000) >> 24); 660706adf6bSPeter Guo 661706adf6bSPeter Guo /* Check Whether subId is 0x11 or 0x12 */ 662706adf6bSPeter Guo if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { 6633665ff03Sernest.zhang scratch_32 = 0x25100000; 664706adf6bSPeter Guo 665706adf6bSPeter Guo o2_pci_set_baseclk(chip, scratch_32); 666706adf6bSPeter Guo ret = pci_read_config_dword(chip->pdev, 667706adf6bSPeter Guo O2_SD_FUNC_REG4, 668706adf6bSPeter Guo &scratch_32); 669706adf6bSPeter Guo 670706adf6bSPeter Guo /* Enable Base Clk setting change */ 671706adf6bSPeter Guo scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; 672706adf6bSPeter Guo pci_write_config_dword(chip->pdev, 673706adf6bSPeter Guo O2_SD_FUNC_REG4, 674706adf6bSPeter Guo scratch_32); 675706adf6bSPeter Guo 676706adf6bSPeter Guo /* Set Tuning Window to 4 */ 677706adf6bSPeter Guo pci_write_config_byte(chip->pdev, 678706adf6bSPeter Guo O2_SD_TUNING_CTRL, 0x44); 679706adf6bSPeter Guo 680706adf6bSPeter Guo break; 681706adf6bSPeter Guo } 682706adf6bSPeter Guo } 683706adf6bSPeter Guo 684706adf6bSPeter Guo /* Enable 8520 led function */ 685706adf6bSPeter Guo o2_pci_led_enable(chip); 686706adf6bSPeter Guo 68701acf691SAdam Lee /* Set timeout CLK */ 68801acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 68901acf691SAdam Lee O2_SD_CLK_SETTING, &scratch_32); 69001acf691SAdam Lee if (ret) 69101acf691SAdam Lee return ret; 69201acf691SAdam Lee 69301acf691SAdam Lee scratch_32 &= ~(0xFF00); 69401acf691SAdam Lee scratch_32 |= 0x07E0C800; 69501acf691SAdam Lee pci_write_config_dword(chip->pdev, 69601acf691SAdam Lee O2_SD_CLK_SETTING, scratch_32); 69701acf691SAdam Lee 69801acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 69901acf691SAdam Lee O2_SD_CLKREQ, &scratch_32); 70001acf691SAdam Lee if (ret) 70101acf691SAdam Lee return ret; 70201acf691SAdam Lee scratch_32 |= 0x3; 70301acf691SAdam Lee pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32); 70401acf691SAdam Lee 70501acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 70601acf691SAdam Lee O2_SD_PLL_SETTING, &scratch_32); 70701acf691SAdam Lee if (ret) 70801acf691SAdam Lee return ret; 70901acf691SAdam Lee 71001acf691SAdam Lee scratch_32 &= ~(0x1F3F070E); 71101acf691SAdam Lee scratch_32 |= 0x18270106; 71201acf691SAdam Lee pci_write_config_dword(chip->pdev, 71301acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 71401acf691SAdam Lee 71501acf691SAdam Lee /* Disable UHS1 funciton */ 71601acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 71701acf691SAdam Lee O2_SD_CAP_REG2, &scratch_32); 71801acf691SAdam Lee if (ret) 71901acf691SAdam Lee return ret; 72001acf691SAdam Lee scratch_32 &= ~(0xE0); 72101acf691SAdam Lee pci_write_config_dword(chip->pdev, 72201acf691SAdam Lee O2_SD_CAP_REG2, scratch_32); 72301acf691SAdam Lee 72401acf691SAdam Lee if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) 72501acf691SAdam Lee sdhci_pci_o2_fujin2_pci_init(chip); 72601acf691SAdam Lee 72701acf691SAdam Lee /* Lock WP */ 72801acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 72901acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 73001acf691SAdam Lee if (ret) 73101acf691SAdam Lee return ret; 73201acf691SAdam Lee scratch |= 0x80; 73301acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 73401acf691SAdam Lee break; 73501acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD0: 73601acf691SAdam Lee case PCI_DEVICE_ID_O2_SEABIRD1: 73701acf691SAdam Lee /* UnLock WP */ 73801acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 73901acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 74001acf691SAdam Lee if (ret) 74101acf691SAdam Lee return ret; 74201acf691SAdam Lee 74301acf691SAdam Lee scratch &= 0x7f; 74401acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 74501acf691SAdam Lee 74601acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 747706adf6bSPeter Guo O2_SD_PLL_SETTING, &scratch_32); 74801acf691SAdam Lee 74901acf691SAdam Lee if ((scratch_32 & 0xff000000) == 0x01000000) { 75001acf691SAdam Lee scratch_32 &= 0x0000FFFF; 75101acf691SAdam Lee scratch_32 |= 0x1F340000; 75201acf691SAdam Lee 75301acf691SAdam Lee pci_write_config_dword(chip->pdev, 75401acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 75501acf691SAdam Lee } else { 75601acf691SAdam Lee scratch_32 &= 0x0000FFFF; 7573665ff03Sernest.zhang scratch_32 |= 0x25100000; 75801acf691SAdam Lee 75901acf691SAdam Lee pci_write_config_dword(chip->pdev, 76001acf691SAdam Lee O2_SD_PLL_SETTING, scratch_32); 76101acf691SAdam Lee 76201acf691SAdam Lee ret = pci_read_config_dword(chip->pdev, 76301acf691SAdam Lee O2_SD_FUNC_REG4, 76401acf691SAdam Lee &scratch_32); 76501acf691SAdam Lee scratch_32 |= (1 << 22); 76601acf691SAdam Lee pci_write_config_dword(chip->pdev, 76701acf691SAdam Lee O2_SD_FUNC_REG4, scratch_32); 76801acf691SAdam Lee } 76901acf691SAdam Lee 770706adf6bSPeter Guo /* Set Tuning Windows to 5 */ 771706adf6bSPeter Guo pci_write_config_byte(chip->pdev, 772706adf6bSPeter Guo O2_SD_TUNING_CTRL, 0x55); 77301acf691SAdam Lee /* Lock WP */ 77401acf691SAdam Lee ret = pci_read_config_byte(chip->pdev, 77501acf691SAdam Lee O2_SD_LOCK_WP, &scratch); 77601acf691SAdam Lee if (ret) 77701acf691SAdam Lee return ret; 77801acf691SAdam Lee scratch |= 0x80; 77901acf691SAdam Lee pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); 78001acf691SAdam Lee break; 78101acf691SAdam Lee } 78201acf691SAdam Lee 78301acf691SAdam Lee return 0; 78401acf691SAdam Lee } 78501acf691SAdam Lee 786b7813f0fSAdrian Hunter #ifdef CONFIG_PM_SLEEP 78701acf691SAdam Lee int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) 78801acf691SAdam Lee { 78901acf691SAdam Lee sdhci_pci_o2_probe(chip); 79030cf2803SAdrian Hunter return sdhci_pci_resume_host(chip); 79101acf691SAdam Lee } 792b7813f0fSAdrian Hunter #endif 793328be8beSErnest Zhang(WH) 79469d91ed1SErnest Zhang(WH) static const struct sdhci_ops sdhci_pci_o2_ops = { 79569d91ed1SErnest Zhang(WH) .set_clock = sdhci_pci_o2_set_clock, 79669d91ed1SErnest Zhang(WH) .enable_dma = sdhci_pci_enable_dma, 79769d91ed1SErnest Zhang(WH) .set_bus_width = sdhci_set_bus_width, 79869d91ed1SErnest Zhang(WH) .reset = sdhci_reset, 79969d91ed1SErnest Zhang(WH) .set_uhs_signaling = sdhci_set_uhs_signaling, 80069d91ed1SErnest Zhang(WH) }; 80169d91ed1SErnest Zhang(WH) 802328be8beSErnest Zhang(WH) const struct sdhci_pci_fixes sdhci_o2 = { 803328be8beSErnest Zhang(WH) .probe = sdhci_pci_o2_probe, 804328be8beSErnest Zhang(WH) .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 805328be8beSErnest Zhang(WH) .probe_slot = sdhci_pci_o2_probe_slot, 806328be8beSErnest Zhang(WH) #ifdef CONFIG_PM_SLEEP 807328be8beSErnest Zhang(WH) .resume = sdhci_pci_o2_resume, 808328be8beSErnest Zhang(WH) #endif 80969d91ed1SErnest Zhang(WH) .ops = &sdhci_pci_o2_ops, 810*7d440617SShirley Her (SC) .priv_size = sizeof(struct o2_host), 811328be8beSErnest Zhang(WH) }; 812