2015-01-15 17:32:35 +03:00
/*
* Copyright ( c ) 2013 - 2015 , Linux Foundation . 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 version 2 and
* only version 2 as published by the Free Software Foundation .
*
* 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 .
*
*/
# ifndef UFS_QCOM_PHY_I_H_
# define UFS_QCOM_PHY_I_H_
2015-01-15 17:32:36 +03:00
# include <linux/module.h>
2015-01-15 17:32:35 +03:00
# include <linux/clk.h>
2015-01-15 17:32:36 +03:00
# include <linux/regulator/consumer.h>
2015-01-15 17:32:35 +03:00
# include <linux/slab.h>
2015-01-15 17:32:36 +03:00
# include <linux/phy/phy-qcom-ufs.h>
2015-01-15 17:32:35 +03:00
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/delay.h>
2015-01-15 17:32:36 +03:00
# define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
( { \
ktime_t timeout = ktime_add_us ( ktime_get ( ) , timeout_us ) ; \
might_sleep_if ( timeout_us ) ; \
for ( ; ; ) { \
( val ) = readl ( addr ) ; \
if ( cond ) \
break ; \
if ( timeout_us & & ktime_compare ( ktime_get ( ) , timeout ) > 0 ) { \
( val ) = readl ( addr ) ; \
break ; \
} \
if ( sleep_us ) \
usleep_range ( DIV_ROUND_UP ( sleep_us , 4 ) , sleep_us ) ; \
} \
( cond ) ? 0 : - ETIMEDOUT ; \
} )
# define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \
{ \
. reg_offset = reg , \
. cfg_value = val , \
}
2015-01-15 17:32:35 +03:00
# define UFS_QCOM_PHY_NAME_LEN 30
2015-01-15 17:32:36 +03:00
enum {
MASK_SERDES_START = 0x1 ,
MASK_PCS_READY = 0x1 ,
} ;
enum {
OFFSET_SERDES_START = 0x0 ,
} ;
struct ufs_qcom_phy_stored_attributes {
u32 att ;
u32 value ;
} ;
2015-01-15 17:32:35 +03:00
struct ufs_qcom_phy_calibration {
u32 reg_offset ;
u32 cfg_value ;
} ;
struct ufs_qcom_phy_vreg {
const char * name ;
struct regulator * reg ;
int max_uA ;
int min_uV ;
int max_uV ;
bool enabled ;
bool is_always_on ;
} ;
struct ufs_qcom_phy {
struct list_head list ;
struct device * dev ;
void __iomem * mmio ;
void __iomem * dev_ref_clk_ctrl_mmio ;
struct clk * tx_iface_clk ;
struct clk * rx_iface_clk ;
bool is_iface_clk_enabled ;
struct clk * ref_clk_src ;
struct clk * ref_clk_parent ;
struct clk * ref_clk ;
bool is_ref_clk_enabled ;
bool is_dev_ref_clk_enabled ;
struct ufs_qcom_phy_vreg vdda_pll ;
struct ufs_qcom_phy_vreg vdda_phy ;
struct ufs_qcom_phy_vreg vddp_ref_clk ;
unsigned int quirks ;
/*
* If UFS link is put into Hibern8 and if UFS PHY analog hardware is
* power collapsed ( by clearing UFS_PHY_POWER_DOWN_CONTROL ) , Hibern8
* exit might fail even after powering on UFS PHY analog hardware .
* Enabling this quirk will help to solve above issue by doing
* custom PHY settings just before PHY analog power collapse .
*/
# define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0)
u8 host_ctrl_rev_major ;
u16 host_ctrl_rev_minor ;
u16 host_ctrl_rev_step ;
char name [ UFS_QCOM_PHY_NAME_LEN ] ;
struct ufs_qcom_phy_calibration * cached_regs ;
int cached_regs_table_size ;
bool is_powered_on ;
struct ufs_qcom_phy_specific_ops * phy_spec_ops ;
} ;
/**
* struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
* specific implementation per phy . Each UFS phy , should implement
* those functions according to its spec and requirements
* @ calibrate_phy : pointer to a function that calibrate the phy
* @ start_serdes : pointer to a function that starts the serdes
* @ is_physical_coding_sublayer_ready : pointer to a function that
* checks pcs readiness . returns 0 for success and non - zero for error .
* @ set_tx_lane_enable : pointer to a function that enable tx lanes
* @ power_control : pointer to a function that controls analog rail of phy
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
*/
struct ufs_qcom_phy_specific_ops {
int ( * calibrate_phy ) ( struct ufs_qcom_phy * phy , bool is_rate_B ) ;
void ( * start_serdes ) ( struct ufs_qcom_phy * phy ) ;
int ( * is_physical_coding_sublayer_ready ) ( struct ufs_qcom_phy * phy ) ;
void ( * set_tx_lane_enable ) ( struct ufs_qcom_phy * phy , u32 val ) ;
void ( * power_control ) ( struct ufs_qcom_phy * phy , bool val ) ;
} ;
struct ufs_qcom_phy * get_ufs_qcom_phy ( struct phy * generic_phy ) ;
int ufs_qcom_phy_power_on ( struct phy * generic_phy ) ;
int ufs_qcom_phy_power_off ( struct phy * generic_phy ) ;
int ufs_qcom_phy_exit ( struct phy * generic_phy ) ;
int ufs_qcom_phy_init_clks ( struct phy * generic_phy ,
struct ufs_qcom_phy * phy_common ) ;
int ufs_qcom_phy_init_vregulators ( struct phy * generic_phy ,
struct ufs_qcom_phy * phy_common ) ;
int ufs_qcom_phy_remove ( struct phy * generic_phy ,
struct ufs_qcom_phy * ufs_qcom_phy ) ;
struct phy * ufs_qcom_phy_generic_probe ( struct platform_device * pdev ,
struct ufs_qcom_phy * common_cfg ,
2015-07-15 10:33:51 +03:00
const struct phy_ops * ufs_qcom_phy_gen_ops ,
2015-01-15 17:32:35 +03:00
struct ufs_qcom_phy_specific_ops * phy_spec_ops ) ;
int ufs_qcom_phy_calibrate ( struct ufs_qcom_phy * ufs_qcom_phy ,
struct ufs_qcom_phy_calibration * tbl_A , int tbl_size_A ,
struct ufs_qcom_phy_calibration * tbl_B , int tbl_size_B ,
bool is_rate_B ) ;
# endif