2013-04-11 06:43:40 +04:00
/* Driver for Realtek PCI-Express card reader
*
* Copyright ( c ) 2009 - 2013 Realtek Semiconductor Corp . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 , or ( at your option ) any
* later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , see < http : //www.gnu.org/licenses/>.
*
* Author :
* Wei WANG < wei_wang @ realsil . com . cn >
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/mfd/rtsx_pci.h>
# include "rtsx_pcr.h"
static u8 rts5249_get_ic_version ( struct rtsx_pcr * pcr )
{
u8 val ;
rtsx_pci_read_register ( pcr , DUMMY_REG_RESET_0 , & val ) ;
return val & 0x0F ;
}
2013-08-20 10:18:51 +04:00
static void rts5249_fill_driving ( struct rtsx_pcr * pcr , u8 voltage )
{
u8 driving_3v3 [ 4 ] [ 3 ] = {
{ 0x11 , 0x11 , 0x11 } ,
{ 0x55 , 0x55 , 0x5C } ,
{ 0x99 , 0x99 , 0x92 } ,
{ 0x99 , 0x99 , 0x92 } ,
} ;
u8 driving_1v8 [ 4 ] [ 3 ] = {
{ 0x3C , 0x3C , 0x3C } ,
{ 0xB3 , 0xB3 , 0xB3 } ,
{ 0xFE , 0xFE , 0xFE } ,
{ 0xC4 , 0xC4 , 0xC4 } ,
} ;
u8 ( * driving ) [ 3 ] , drive_sel ;
if ( voltage = = OUTPUT_3V3 ) {
driving = driving_3v3 ;
drive_sel = pcr - > sd30_drive_sel_3v3 ;
} else {
driving = driving_1v8 ;
drive_sel = pcr - > sd30_drive_sel_1v8 ;
}
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , SD30_CLK_DRIVE_SEL ,
0xFF , driving [ drive_sel ] [ 0 ] ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , SD30_CMD_DRIVE_SEL ,
0xFF , driving [ drive_sel ] [ 1 ] ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , SD30_DAT_DRIVE_SEL ,
0xFF , driving [ drive_sel ] [ 2 ] ) ;
}
static void rts5249_fetch_vendor_settings ( struct rtsx_pcr * pcr )
{
u32 reg ;
rtsx_pci_read_config_dword ( pcr , PCR_SETTING_REG1 , & reg ) ;
dev_dbg ( & ( pcr - > pci - > dev ) , " Cfg 0x%x: 0x%x \n " , PCR_SETTING_REG1 , reg ) ;
if ( ! rtsx_vendor_setting_valid ( reg ) )
return ;
pcr - > aspm_en = rtsx_reg_to_aspm ( reg ) ;
pcr - > sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8 ( reg ) ;
pcr - > card_drive_sel & = 0x3F ;
pcr - > card_drive_sel | = rtsx_reg_to_card_drive_sel ( reg ) ;
rtsx_pci_read_config_dword ( pcr , PCR_SETTING_REG2 , & reg ) ;
dev_dbg ( & ( pcr - > pci - > dev ) , " Cfg 0x%x: 0x%x \n " , PCR_SETTING_REG2 , reg ) ;
pcr - > sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3 ( reg ) ;
if ( rtsx_reg_check_reverse_socket ( reg ) )
pcr - > flags | = PCR_REVERSE_SOCKET ;
}
2013-08-20 10:18:55 +04:00
static void rts5249_force_power_down ( struct rtsx_pcr * pcr , u8 pm_state )
2013-08-20 10:18:52 +04:00
{
/* Set relink_time to 0 */
rtsx_pci_write_register ( pcr , AUTOLOAD_CFG_BASE + 1 , 0xFF , 0 ) ;
rtsx_pci_write_register ( pcr , AUTOLOAD_CFG_BASE + 2 , 0xFF , 0 ) ;
rtsx_pci_write_register ( pcr , AUTOLOAD_CFG_BASE + 3 , 0x01 , 0 ) ;
2013-08-20 10:18:55 +04:00
if ( pm_state = = HOST_ENTER_S3 )
rtsx_pci_write_register ( pcr , PM_CTRL3 , 0x10 , 0x10 ) ;
2013-08-20 10:18:52 +04:00
rtsx_pci_write_register ( pcr , FPDCTL , 0x03 , 0x03 ) ;
}
2013-04-11 06:43:40 +04:00
static int rts5249_extra_init_hw ( struct rtsx_pcr * pcr )
{
rtsx_pci_init_cmd ( pcr ) ;
/* Configure GPIO as output */
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , GPIO_CTL , 0x02 , 0x02 ) ;
2013-08-20 10:18:53 +04:00
/* Reset ASPM state to default value */
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , ASPM_FORCE_CTL , 0x3F , 0 ) ;
2013-04-11 06:43:40 +04:00
/* Switch LDO3318 source from DV33 to card_3v3 */
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , LDO_PWR_SEL , 0x03 , 0x00 ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , LDO_PWR_SEL , 0x03 , 0x01 ) ;
/* LED shine disabled, set initial shine cycle period */
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , OLT_LED_CTL , 0x0F , 0x02 ) ;
2013-08-20 10:18:51 +04:00
/* Configure driving */
rts5249_fill_driving ( pcr , OUTPUT_3V3 ) ;
if ( pcr - > flags & PCR_REVERSE_SOCKET )
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD ,
AUTOLOAD_CFG_BASE + 3 , 0xB0 , 0xB0 ) ;
else
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD ,
AUTOLOAD_CFG_BASE + 3 , 0xB0 , 0x80 ) ;
2013-08-20 10:18:55 +04:00
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , PM_CTRL3 , 0x10 , 0x00 ) ;
2013-04-11 06:43:40 +04:00
return rtsx_pci_send_cmd ( pcr , 100 ) ;
}
static int rts5249_optimize_phy ( struct rtsx_pcr * pcr )
{
int err ;
2014-10-10 09:58:44 +04:00
err = rtsx_gops_pm_reset ( pcr ) ;
if ( err < 0 )
return err ;
2013-09-13 13:45:43 +04:00
err = rtsx_pci_write_phy_register ( pcr , PHY_REG_REV ,
PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED |
PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN |
PHY_REG_REV_RX_PWST | PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 |
PHY_REG_REV_STOP_CLKRD | PHY_REG_REV_STOP_CLKWR ) ;
2013-04-11 06:43:40 +04:00
if ( err < 0 )
return err ;
msleep ( 1 ) ;
2013-09-13 13:45:43 +04:00
err = rtsx_pci_write_phy_register ( pcr , PHY_BPCR ,
PHY_BPCR_IBRXSEL | PHY_BPCR_IBTXSEL |
PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_PCR ,
PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 |
PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 |
PHY_PCR_RSSI_EN ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_RCR2 ,
PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR |
PHY_RCR2_CDR_CP_10 | PHY_RCR2_CDR_SR_2 |
PHY_RCR2_FREQSEL_12 | PHY_RCR2_CPADJEN |
PHY_RCR2_CDR_SC_8 | PHY_RCR2_CALIB_LATE ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_FLD4 ,
PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF |
PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA |
PHY_FLD4_BER_COUNT | PHY_FLD4_BER_TIMER |
PHY_FLD4_BER_CHK_EN ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_RDR , PHY_RDR_RXDSEL_1_9 ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_RCR1 ,
PHY_RCR1_ADP_TIME | PHY_RCR1_VCO_COARSE ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_FLD3 ,
PHY_FLD3_TIMER_4 | PHY_FLD3_TIMER_6 |
PHY_FLD3_RXDELINK ) ;
if ( err < 0 )
return err ;
return rtsx_pci_write_phy_register ( pcr , PHY_TUNE ,
PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 |
PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 |
PHY_TUNE_TUNED12 ) ;
2013-04-11 06:43:40 +04:00
}
static int rts5249_turn_on_led ( struct rtsx_pcr * pcr )
{
return rtsx_pci_write_register ( pcr , GPIO_CTL , 0x02 , 0x02 ) ;
}
static int rts5249_turn_off_led ( struct rtsx_pcr * pcr )
{
return rtsx_pci_write_register ( pcr , GPIO_CTL , 0x02 , 0x00 ) ;
}
static int rts5249_enable_auto_blink ( struct rtsx_pcr * pcr )
{
return rtsx_pci_write_register ( pcr , OLT_LED_CTL , 0x08 , 0x08 ) ;
}
static int rts5249_disable_auto_blink ( struct rtsx_pcr * pcr )
{
return rtsx_pci_write_register ( pcr , OLT_LED_CTL , 0x08 , 0x00 ) ;
}
static int rts5249_card_power_on ( struct rtsx_pcr * pcr , int card )
{
int err ;
rtsx_pci_init_cmd ( pcr ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , CARD_PWR_CTL ,
SD_POWER_MASK , SD_VCC_PARTIAL_POWER_ON ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , PWR_GATE_CTRL ,
LDO3318_PWR_MASK , 0x02 ) ;
err = rtsx_pci_send_cmd ( pcr , 100 ) ;
if ( err < 0 )
return err ;
msleep ( 5 ) ;
rtsx_pci_init_cmd ( pcr ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , CARD_PWR_CTL ,
SD_POWER_MASK , SD_VCC_POWER_ON ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , PWR_GATE_CTRL ,
LDO3318_PWR_MASK , 0x06 ) ;
err = rtsx_pci_send_cmd ( pcr , 100 ) ;
if ( err < 0 )
return err ;
return 0 ;
}
static int rts5249_card_power_off ( struct rtsx_pcr * pcr , int card )
{
rtsx_pci_init_cmd ( pcr ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , CARD_PWR_CTL ,
SD_POWER_MASK , SD_POWER_OFF ) ;
rtsx_pci_add_cmd ( pcr , WRITE_REG_CMD , PWR_GATE_CTRL ,
LDO3318_PWR_MASK , 0x00 ) ;
return rtsx_pci_send_cmd ( pcr , 100 ) ;
}
static int rts5249_switch_output_voltage ( struct rtsx_pcr * pcr , u8 voltage )
{
int err ;
if ( voltage = = OUTPUT_3V3 ) {
err = rtsx_pci_write_phy_register ( pcr , PHY_TUNE , 0x4FC0 | 0x24 ) ;
if ( err < 0 )
return err ;
} else if ( voltage = = OUTPUT_1V8 ) {
err = rtsx_pci_write_phy_register ( pcr , PHY_BACR , 0x3C02 ) ;
if ( err < 0 )
return err ;
err = rtsx_pci_write_phy_register ( pcr , PHY_TUNE , 0x4C40 | 0x24 ) ;
if ( err < 0 )
return err ;
} else {
return - EINVAL ;
}
/* set pad drive */
rtsx_pci_init_cmd ( pcr ) ;
2013-08-20 10:18:51 +04:00
rts5249_fill_driving ( pcr , voltage ) ;
2013-04-11 06:43:40 +04:00
return rtsx_pci_send_cmd ( pcr , 100 ) ;
}
static const struct pcr_ops rts5249_pcr_ops = {
2013-08-20 10:18:51 +04:00
. fetch_vendor_settings = rts5249_fetch_vendor_settings ,
2013-04-11 06:43:40 +04:00
. extra_init_hw = rts5249_extra_init_hw ,
. optimize_phy = rts5249_optimize_phy ,
. turn_on_led = rts5249_turn_on_led ,
. turn_off_led = rts5249_turn_off_led ,
. enable_auto_blink = rts5249_enable_auto_blink ,
. disable_auto_blink = rts5249_disable_auto_blink ,
. card_power_on = rts5249_card_power_on ,
. card_power_off = rts5249_card_power_off ,
. switch_output_voltage = rts5249_switch_output_voltage ,
2013-08-20 10:18:52 +04:00
. force_power_down = rts5249_force_power_down ,
2013-04-11 06:43:40 +04:00
} ;
/* SD Pull Control Enable:
* SD_DAT [ 3 : 0 ] = = > pull up
* SD_CD = = > pull up
* SD_WP = = > pull up
* SD_CMD = = > pull up
* SD_CLK = = > pull down
*/
static const u32 rts5249_sd_pull_ctl_enable_tbl [ ] = {
RTSX_REG_PAIR ( CARD_PULL_CTL1 , 0x66 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL2 , 0xAA ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL3 , 0xE9 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL4 , 0xAA ) ,
0 ,
} ;
/* SD Pull Control Disable:
* SD_DAT [ 3 : 0 ] = = > pull down
* SD_CD = = > pull up
* SD_WP = = > pull down
* SD_CMD = = > pull down
* SD_CLK = = > pull down
*/
static const u32 rts5249_sd_pull_ctl_disable_tbl [ ] = {
RTSX_REG_PAIR ( CARD_PULL_CTL1 , 0x66 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL2 , 0x55 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL3 , 0xD5 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL4 , 0x55 ) ,
0 ,
} ;
/* MS Pull Control Enable:
* MS CD = = > pull up
* others = = > pull down
*/
static const u32 rts5249_ms_pull_ctl_enable_tbl [ ] = {
RTSX_REG_PAIR ( CARD_PULL_CTL4 , 0x55 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL5 , 0x55 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL6 , 0x15 ) ,
0 ,
} ;
/* MS Pull Control Disable:
* MS CD = = > pull up
* others = = > pull down
*/
static const u32 rts5249_ms_pull_ctl_disable_tbl [ ] = {
RTSX_REG_PAIR ( CARD_PULL_CTL4 , 0x55 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL5 , 0x55 ) ,
RTSX_REG_PAIR ( CARD_PULL_CTL6 , 0x15 ) ,
0 ,
} ;
void rts5249_init_params ( struct rtsx_pcr * pcr )
{
pcr - > extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104 ;
pcr - > num_slots = 2 ;
pcr - > ops = & rts5249_pcr_ops ;
2013-08-20 10:18:51 +04:00
pcr - > flags = 0 ;
pcr - > card_drive_sel = RTSX_CARD_DRIVE_DEFAULT ;
pcr - > sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_C ;
pcr - > sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B ;
pcr - > aspm_en = ASPM_L1_EN ;
2013-08-21 05:46:25 +04:00
pcr - > tx_initial_phase = SET_CLOCK_PHASE ( 1 , 29 , 16 ) ;
pcr - > rx_initial_phase = SET_CLOCK_PHASE ( 24 , 6 , 5 ) ;
2013-08-20 10:18:51 +04:00
2013-04-11 06:43:40 +04:00
pcr - > ic_version = rts5249_get_ic_version ( pcr ) ;
pcr - > sd_pull_ctl_enable_tbl = rts5249_sd_pull_ctl_enable_tbl ;
pcr - > sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl ;
pcr - > ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl ;
pcr - > ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl ;
}