2022-03-01 16:46:18 +05:30
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2022 Texas Instruments Incorporated - https : //www.ti.com/
*/
# include <linux/bitfield.h>
# include <linux/bitops.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/module.h>
# include <linux/phy/phy.h>
# include <linux/phy/phy-mipi-dphy.h>
# include <linux/platform_device.h>
2023-03-14 13:01:37 +05:30
# include <linux/sys_soc.h>
2022-03-01 16:46:18 +05:30
# define DPHY_PMA_CMN(reg) (reg)
# define DPHY_PCS(reg) (0xb00 + (reg))
# define DPHY_ISO(reg) (0xc00 + (reg))
2023-03-14 13:01:37 +05:30
# define DPHY_WRAP(reg) (0x1000 + (reg))
2022-03-01 16:46:18 +05:30
# define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
# define DPHY_CMN_RX_MODE_EN BIT(10)
# define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1)
# define DPHY_CMN_SSM_EN BIT(0)
# define DPHY_CMN_RX_BANDGAP_TIMER 0x14
# define DPHY_BAND_CFG DPHY_PCS(0x0)
# define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5)
# define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0)
# define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8)
# define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa
# define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc)
# define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa
2023-03-14 13:01:37 +05:30
# define DPHY_LANE DPHY_WRAP(0x0)
# define DPHY_LANE_RESET_CMN_EN BIT(23)
2022-03-01 16:46:18 +05:30
# define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10)
# define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14)
# define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20)
# define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30)
# define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c)
# define DPHY_ISO_LANE_READY_BIT 0
# define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL
# define DPHY_LANES_MIN 1
# define DPHY_LANES_MAX 4
struct cdns_dphy_rx {
void __iomem * regs ;
struct device * dev ;
struct phy * phy ;
} ;
struct cdns_dphy_rx_band {
/* Rates are in Mbps. */
unsigned int min_rate ;
unsigned int max_rate ;
} ;
2023-03-14 13:01:37 +05:30
struct cdns_dphy_soc_data {
bool has_hw_cmn_rstb ;
} ;
2022-03-01 16:46:18 +05:30
/* Order of bands is important since the index is the band number. */
static const struct cdns_dphy_rx_band bands [ ] = {
{ 80 , 100 } , { 100 , 120 } , { 120 , 160 } , { 160 , 200 } , { 200 , 240 } ,
{ 240 , 280 } , { 280 , 320 } , { 320 , 360 } , { 360 , 400 } , { 400 , 480 } ,
{ 480 , 560 } , { 560 , 640 } , { 640 , 720 } , { 720 , 800 } , { 800 , 880 } ,
{ 880 , 1040 } , { 1040 , 1200 } , { 1200 , 1350 } , { 1350 , 1500 } ,
{ 1500 , 1750 } , { 1750 , 2000 } , { 2000 , 2250 } , { 2250 , 2500 }
} ;
static int cdns_dphy_rx_power_on ( struct phy * phy )
{
struct cdns_dphy_rx * dphy = phy_get_drvdata ( phy ) ;
/* Start RX state machine. */
writel ( DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN |
FIELD_PREP ( DPHY_CMN_RX_BANDGAP_TIMER_MASK ,
DPHY_CMN_RX_BANDGAP_TIMER ) ,
dphy - > regs + DPHY_CMN_SSM ) ;
return 0 ;
}
static int cdns_dphy_rx_power_off ( struct phy * phy )
{
struct cdns_dphy_rx * dphy = phy_get_drvdata ( phy ) ;
writel ( 0 , dphy - > regs + DPHY_CMN_SSM ) ;
return 0 ;
}
static int cdns_dphy_rx_get_band_ctrl ( unsigned long hs_clk_rate )
{
unsigned int rate , i ;
rate = hs_clk_rate / 1000000UL ;
/* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */
rate * = 2 ;
if ( rate < bands [ 0 ] . min_rate )
return - EOPNOTSUPP ;
for ( i = 0 ; i < ARRAY_SIZE ( bands ) ; i + + )
if ( rate < bands [ i ] . max_rate )
return i ;
return - EOPNOTSUPP ;
}
static inline int cdns_dphy_rx_wait_for_bit ( void __iomem * addr ,
unsigned int bit )
{
u32 val ;
return readl_relaxed_poll_timeout ( addr , val , val & BIT ( bit ) , 10 ,
DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000 ) ;
}
static int cdns_dphy_rx_wait_lane_ready ( struct cdns_dphy_rx * dphy ,
unsigned int lanes )
{
static const u32 data_lane_ctrl [ ] = { DPHY_ISO_DL_CTRL_L0 ,
DPHY_ISO_DL_CTRL_L1 ,
DPHY_ISO_DL_CTRL_L2 ,
DPHY_ISO_DL_CTRL_L3 } ;
void __iomem * reg = dphy - > regs ;
unsigned int i ;
int ret ;
/* Clock lane */
ret = cdns_dphy_rx_wait_for_bit ( reg + DPHY_ISO_CL_CTRL_L ,
DPHY_ISO_LANE_READY_BIT ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < lanes ; i + + ) {
ret = cdns_dphy_rx_wait_for_bit ( reg + data_lane_ctrl [ i ] ,
DPHY_ISO_LANE_READY_BIT ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2023-03-14 13:01:37 +05:30
static struct cdns_dphy_soc_data j721e_soc_data = {
. has_hw_cmn_rstb = true ,
} ;
static const struct soc_device_attribute cdns_dphy_socinfo [ ] = {
{
. family = " J721E " ,
. revision = " SR1.0 " ,
. data = & j721e_soc_data ,
} ,
{ /* sentinel */ }
} ;
2022-03-01 16:46:18 +05:30
static int cdns_dphy_rx_configure ( struct phy * phy ,
union phy_configure_opts * opts )
{
struct cdns_dphy_rx * dphy = phy_get_drvdata ( phy ) ;
unsigned int reg , lanes = opts - > mipi_dphy . lanes ;
2023-03-14 13:01:37 +05:30
const struct cdns_dphy_soc_data * soc_data = NULL ;
const struct soc_device_attribute * soc ;
2022-03-01 16:46:18 +05:30
int band_ctrl , ret ;
2023-03-14 13:01:37 +05:30
soc = soc_device_match ( cdns_dphy_socinfo ) ;
if ( soc & & soc - > data )
soc_data = soc - > data ;
if ( ! soc | | ( soc_data & & ! soc_data - > has_hw_cmn_rstb ) ) {
reg = DPHY_LANE_RESET_CMN_EN ;
writel ( reg , dphy - > regs + DPHY_LANE ) ;
}
2022-03-01 16:46:18 +05:30
/* Data lanes. Minimum one lane is mandatory. */
if ( lanes < DPHY_LANES_MIN | | lanes > DPHY_LANES_MAX )
return - EINVAL ;
band_ctrl = cdns_dphy_rx_get_band_ctrl ( opts - > mipi_dphy . hs_clk_rate ) ;
if ( band_ctrl < 0 )
return band_ctrl ;
reg = FIELD_PREP ( DPHY_BAND_CFG_LEFT_BAND , band_ctrl ) |
FIELD_PREP ( DPHY_BAND_CFG_RIGHT_BAND , band_ctrl ) ;
writel ( reg , dphy - > regs + DPHY_BAND_CFG ) ;
/*
* Set the required power island phase 2 time . This is mandated by DPHY
* specs .
*/
reg = DPHY_POWER_ISLAND_EN_DATA_VAL ;
writel ( reg , dphy - > regs + DPHY_POWER_ISLAND_EN_DATA ) ;
reg = DPHY_POWER_ISLAND_EN_CLK_VAL ;
writel ( reg , dphy - > regs + DPHY_POWER_ISLAND_EN_CLK ) ;
ret = cdns_dphy_rx_wait_lane_ready ( dphy , lanes ) ;
if ( ret ) {
dev_err ( dphy - > dev , " DPHY wait for lane ready timeout \n " ) ;
return ret ;
}
return 0 ;
}
static int cdns_dphy_rx_validate ( struct phy * phy , enum phy_mode mode ,
int submode , union phy_configure_opts * opts )
{
int ret ;
if ( mode ! = PHY_MODE_MIPI_DPHY )
return - EINVAL ;
ret = cdns_dphy_rx_get_band_ctrl ( opts - > mipi_dphy . hs_clk_rate ) ;
if ( ret < 0 )
return ret ;
return phy_mipi_dphy_config_validate ( & opts - > mipi_dphy ) ;
}
static const struct phy_ops cdns_dphy_rx_ops = {
. power_on = cdns_dphy_rx_power_on ,
. power_off = cdns_dphy_rx_power_off ,
. configure = cdns_dphy_rx_configure ,
. validate = cdns_dphy_rx_validate ,
} ;
static int cdns_dphy_rx_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct phy_provider * provider ;
struct cdns_dphy_rx * dphy ;
dphy = devm_kzalloc ( dev , sizeof ( * dphy ) , GFP_KERNEL ) ;
if ( ! dphy )
return - ENOMEM ;
dev_set_drvdata ( dev , dphy ) ;
dphy - > dev = dev ;
dphy - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( dphy - > regs ) )
return PTR_ERR ( dphy - > regs ) ;
dphy - > phy = devm_phy_create ( dev , NULL , & cdns_dphy_rx_ops ) ;
if ( IS_ERR ( dphy - > phy ) ) {
dev_err ( dev , " Failed to create PHY: %ld \n " , PTR_ERR ( dphy - > phy ) ) ;
return PTR_ERR ( dphy - > phy ) ;
}
phy_set_drvdata ( dphy - > phy , dphy ) ;
provider = devm_of_phy_provider_register ( dev , of_phy_simple_xlate ) ;
if ( IS_ERR ( provider ) ) {
dev_err ( dev , " Failed to register PHY provider: %ld \n " ,
PTR_ERR ( provider ) ) ;
return PTR_ERR ( provider ) ;
}
return 0 ;
}
static const struct of_device_id cdns_dphy_rx_of_match [ ] = {
{ . compatible = " cdns,dphy-rx " } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , cdns_dphy_rx_of_match ) ;
static struct platform_driver cdns_dphy_rx_platform_driver = {
. probe = cdns_dphy_rx_probe ,
. driver = {
. name = " cdns-mipi-dphy-rx " ,
. of_match_table = cdns_dphy_rx_of_match ,
} ,
} ;
module_platform_driver ( cdns_dphy_rx_platform_driver ) ;
MODULE_AUTHOR ( " Pratyush Yadav <p.yadav@ti.com> " ) ;
MODULE_DESCRIPTION ( " Cadence D-PHY Rx Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;