1*aaf4989bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2e455b69dSRui Feng /* Driver for Realtek PCI-Express card reader 3e455b69dSRui Feng * 4e455b69dSRui Feng * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. 5e455b69dSRui Feng * 6e455b69dSRui Feng * Author: 7e455b69dSRui Feng * Wei WANG <wei_wang@realsil.com.cn> 8e455b69dSRui Feng */ 9e455b69dSRui Feng 10e455b69dSRui Feng #include <linux/module.h> 11e455b69dSRui Feng #include <linux/delay.h> 12e455b69dSRui Feng #include <linux/rtsx_pci.h> 13e455b69dSRui Feng 14e455b69dSRui Feng #include "rtsx_pcr.h" 15e455b69dSRui Feng 16e455b69dSRui Feng static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr) 17e455b69dSRui Feng { 18e455b69dSRui Feng u8 val; 19e455b69dSRui Feng 20e455b69dSRui Feng rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); 21e455b69dSRui Feng return val & 0x0F; 22e455b69dSRui Feng } 23e455b69dSRui Feng 24e455b69dSRui Feng static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) 25e455b69dSRui Feng { 26e455b69dSRui Feng u32 reg; 27e455b69dSRui Feng 28e455b69dSRui Feng rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); 29e455b69dSRui Feng pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); 30e455b69dSRui Feng 31e455b69dSRui Feng if (!rtsx_vendor_setting_valid(reg)) 32e455b69dSRui Feng return; 33e455b69dSRui Feng 34e455b69dSRui Feng pcr->aspm_en = rtsx_reg_to_aspm(reg); 35e455b69dSRui Feng pcr->sd30_drive_sel_1v8 = 36e455b69dSRui Feng map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg)); 37e455b69dSRui Feng pcr->card_drive_sel &= 0x3F; 38e455b69dSRui Feng pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); 39e455b69dSRui Feng 40e455b69dSRui Feng rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); 41e455b69dSRui Feng pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); 42e455b69dSRui Feng pcr->sd30_drive_sel_3v3 = 43e455b69dSRui Feng map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); 44e455b69dSRui Feng } 45e455b69dSRui Feng 46e455b69dSRui Feng static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) 47e455b69dSRui Feng { 48e455b69dSRui Feng rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); 49e455b69dSRui Feng } 50e455b69dSRui Feng 51e455b69dSRui Feng static int rts5229_extra_init_hw(struct rtsx_pcr *pcr) 52e455b69dSRui Feng { 53e455b69dSRui Feng rtsx_pci_init_cmd(pcr); 54e455b69dSRui Feng 55e455b69dSRui Feng /* Configure GPIO as output */ 56e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); 57e455b69dSRui Feng /* Reset ASPM state to default value */ 58e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0); 59e455b69dSRui Feng /* Force CLKREQ# PIN to drive 0 to request clock */ 60e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08); 61e455b69dSRui Feng /* Switch LDO3318 source from DV33 to card_3v3 */ 62e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); 63e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); 64e455b69dSRui Feng /* LED shine disabled, set initial shine cycle period */ 65e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); 66e455b69dSRui Feng /* Configure driving */ 67e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, 68e455b69dSRui Feng 0xFF, pcr->sd30_drive_sel_3v3); 69e455b69dSRui Feng 70e455b69dSRui Feng return rtsx_pci_send_cmd(pcr, 100); 71e455b69dSRui Feng } 72e455b69dSRui Feng 73e455b69dSRui Feng static int rts5229_optimize_phy(struct rtsx_pcr *pcr) 74e455b69dSRui Feng { 75e455b69dSRui Feng /* Optimize RX sensitivity */ 76e455b69dSRui Feng return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); 77e455b69dSRui Feng } 78e455b69dSRui Feng 79e455b69dSRui Feng static int rts5229_turn_on_led(struct rtsx_pcr *pcr) 80e455b69dSRui Feng { 81e455b69dSRui Feng return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); 82e455b69dSRui Feng } 83e455b69dSRui Feng 84e455b69dSRui Feng static int rts5229_turn_off_led(struct rtsx_pcr *pcr) 85e455b69dSRui Feng { 86e455b69dSRui Feng return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); 87e455b69dSRui Feng } 88e455b69dSRui Feng 89e455b69dSRui Feng static int rts5229_enable_auto_blink(struct rtsx_pcr *pcr) 90e455b69dSRui Feng { 91e455b69dSRui Feng return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); 92e455b69dSRui Feng } 93e455b69dSRui Feng 94e455b69dSRui Feng static int rts5229_disable_auto_blink(struct rtsx_pcr *pcr) 95e455b69dSRui Feng { 96e455b69dSRui Feng return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); 97e455b69dSRui Feng } 98e455b69dSRui Feng 99e455b69dSRui Feng static int rts5229_card_power_on(struct rtsx_pcr *pcr, int card) 100e455b69dSRui Feng { 101e455b69dSRui Feng int err; 102e455b69dSRui Feng 103e455b69dSRui Feng rtsx_pci_init_cmd(pcr); 104e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, 105e455b69dSRui Feng SD_POWER_MASK, SD_PARTIAL_POWER_ON); 106e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, 107e455b69dSRui Feng LDO3318_PWR_MASK, 0x02); 108e455b69dSRui Feng err = rtsx_pci_send_cmd(pcr, 100); 109e455b69dSRui Feng if (err < 0) 110e455b69dSRui Feng return err; 111e455b69dSRui Feng 112e455b69dSRui Feng /* To avoid too large in-rush current */ 113e455b69dSRui Feng udelay(150); 114e455b69dSRui Feng 115e455b69dSRui Feng rtsx_pci_init_cmd(pcr); 116e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, 117e455b69dSRui Feng SD_POWER_MASK, SD_POWER_ON); 118e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, 119e455b69dSRui Feng LDO3318_PWR_MASK, 0x06); 120e455b69dSRui Feng return rtsx_pci_send_cmd(pcr, 100); 121e455b69dSRui Feng } 122e455b69dSRui Feng 123e455b69dSRui Feng static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card) 124e455b69dSRui Feng { 125e455b69dSRui Feng rtsx_pci_init_cmd(pcr); 126e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, 127e455b69dSRui Feng SD_POWER_MASK | PMOS_STRG_MASK, 128e455b69dSRui Feng SD_POWER_OFF | PMOS_STRG_400mA); 129e455b69dSRui Feng rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, 130e455b69dSRui Feng LDO3318_PWR_MASK, 0x00); 131e455b69dSRui Feng return rtsx_pci_send_cmd(pcr, 100); 132e455b69dSRui Feng } 133e455b69dSRui Feng 134e455b69dSRui Feng static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) 135e455b69dSRui Feng { 136e455b69dSRui Feng int err; 137e455b69dSRui Feng 138e455b69dSRui Feng if (voltage == OUTPUT_3V3) { 139e455b69dSRui Feng err = rtsx_pci_write_register(pcr, 140e455b69dSRui Feng SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); 141e455b69dSRui Feng if (err < 0) 142e455b69dSRui Feng return err; 143e455b69dSRui Feng err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); 144e455b69dSRui Feng if (err < 0) 145e455b69dSRui Feng return err; 146e455b69dSRui Feng } else if (voltage == OUTPUT_1V8) { 147e455b69dSRui Feng err = rtsx_pci_write_register(pcr, 148e455b69dSRui Feng SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); 149e455b69dSRui Feng if (err < 0) 150e455b69dSRui Feng return err; 151e455b69dSRui Feng err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); 152e455b69dSRui Feng if (err < 0) 153e455b69dSRui Feng return err; 154e455b69dSRui Feng } else { 155e455b69dSRui Feng return -EINVAL; 156e455b69dSRui Feng } 157e455b69dSRui Feng 158e455b69dSRui Feng return 0; 159e455b69dSRui Feng } 160e455b69dSRui Feng 161e455b69dSRui Feng static const struct pcr_ops rts5229_pcr_ops = { 162e455b69dSRui Feng .fetch_vendor_settings = rts5229_fetch_vendor_settings, 163e455b69dSRui Feng .extra_init_hw = rts5229_extra_init_hw, 164e455b69dSRui Feng .optimize_phy = rts5229_optimize_phy, 165e455b69dSRui Feng .turn_on_led = rts5229_turn_on_led, 166e455b69dSRui Feng .turn_off_led = rts5229_turn_off_led, 167e455b69dSRui Feng .enable_auto_blink = rts5229_enable_auto_blink, 168e455b69dSRui Feng .disable_auto_blink = rts5229_disable_auto_blink, 169e455b69dSRui Feng .card_power_on = rts5229_card_power_on, 170e455b69dSRui Feng .card_power_off = rts5229_card_power_off, 171e455b69dSRui Feng .switch_output_voltage = rts5229_switch_output_voltage, 172e455b69dSRui Feng .cd_deglitch = NULL, 173e455b69dSRui Feng .conv_clk_and_div_n = NULL, 174e455b69dSRui Feng .force_power_down = rts5229_force_power_down, 175e455b69dSRui Feng }; 176e455b69dSRui Feng 177e455b69dSRui Feng /* SD Pull Control Enable: 178e455b69dSRui Feng * SD_DAT[3:0] ==> pull up 179e455b69dSRui Feng * SD_CD ==> pull up 180e455b69dSRui Feng * SD_WP ==> pull up 181e455b69dSRui Feng * SD_CMD ==> pull up 182e455b69dSRui Feng * SD_CLK ==> pull down 183e455b69dSRui Feng */ 184e455b69dSRui Feng static const u32 rts5229_sd_pull_ctl_enable_tbl1[] = { 185e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), 186e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), 187e455b69dSRui Feng 0, 188e455b69dSRui Feng }; 189e455b69dSRui Feng 190e455b69dSRui Feng /* For RTS5229 version C */ 191e455b69dSRui Feng static const u32 rts5229_sd_pull_ctl_enable_tbl2[] = { 192e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), 193e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD9), 194e455b69dSRui Feng 0, 195e455b69dSRui Feng }; 196e455b69dSRui Feng 197e455b69dSRui Feng /* SD Pull Control Disable: 198e455b69dSRui Feng * SD_DAT[3:0] ==> pull down 199e455b69dSRui Feng * SD_CD ==> pull up 200e455b69dSRui Feng * SD_WP ==> pull down 201e455b69dSRui Feng * SD_CMD ==> pull down 202e455b69dSRui Feng * SD_CLK ==> pull down 203e455b69dSRui Feng */ 204e455b69dSRui Feng static const u32 rts5229_sd_pull_ctl_disable_tbl1[] = { 205e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), 206e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), 207e455b69dSRui Feng 0, 208e455b69dSRui Feng }; 209e455b69dSRui Feng 210e455b69dSRui Feng /* For RTS5229 version C */ 211e455b69dSRui Feng static const u32 rts5229_sd_pull_ctl_disable_tbl2[] = { 212e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), 213e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE5), 214e455b69dSRui Feng 0, 215e455b69dSRui Feng }; 216e455b69dSRui Feng 217e455b69dSRui Feng /* MS Pull Control Enable: 218e455b69dSRui Feng * MS CD ==> pull up 219e455b69dSRui Feng * others ==> pull down 220e455b69dSRui Feng */ 221e455b69dSRui Feng static const u32 rts5229_ms_pull_ctl_enable_tbl[] = { 222e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), 223e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), 224e455b69dSRui Feng 0, 225e455b69dSRui Feng }; 226e455b69dSRui Feng 227e455b69dSRui Feng /* MS Pull Control Disable: 228e455b69dSRui Feng * MS CD ==> pull up 229e455b69dSRui Feng * others ==> pull down 230e455b69dSRui Feng */ 231e455b69dSRui Feng static const u32 rts5229_ms_pull_ctl_disable_tbl[] = { 232e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), 233e455b69dSRui Feng RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), 234e455b69dSRui Feng 0, 235e455b69dSRui Feng }; 236e455b69dSRui Feng 237e455b69dSRui Feng void rts5229_init_params(struct rtsx_pcr *pcr) 238e455b69dSRui Feng { 239e455b69dSRui Feng pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; 240e455b69dSRui Feng pcr->num_slots = 2; 241e455b69dSRui Feng pcr->ops = &rts5229_pcr_ops; 242e455b69dSRui Feng 243e455b69dSRui Feng pcr->flags = 0; 244e455b69dSRui Feng pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; 245e455b69dSRui Feng pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; 246e455b69dSRui Feng pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; 247e455b69dSRui Feng pcr->aspm_en = ASPM_L1_EN; 248e455b69dSRui Feng pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 15); 249e455b69dSRui Feng pcr->rx_initial_phase = SET_CLOCK_PHASE(30, 6, 6); 250e455b69dSRui Feng 251e455b69dSRui Feng pcr->ic_version = rts5229_get_ic_version(pcr); 252e455b69dSRui Feng if (pcr->ic_version == IC_VER_C) { 253e455b69dSRui Feng pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl2; 254e455b69dSRui Feng pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl2; 255e455b69dSRui Feng } else { 256e455b69dSRui Feng pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl1; 257e455b69dSRui Feng pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl1; 258e455b69dSRui Feng } 259e455b69dSRui Feng pcr->ms_pull_ctl_enable_tbl = rts5229_ms_pull_ctl_enable_tbl; 260e455b69dSRui Feng pcr->ms_pull_ctl_disable_tbl = rts5229_ms_pull_ctl_disable_tbl; 261e455b69dSRui Feng } 262