2019-06-01 11:09:00 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-10-01 15:19:00 +03:00
/*
2017-06-01 16:11:14 +03:00
* Meson8 , Meson8b and GXBB USB2 PHY driver
2016-10-01 15:19:00 +03:00
*
* Copyright ( C ) 2016 Martin Blumenstingl < martin . blumenstingl @ googlemail . com >
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of_device.h>
2020-05-13 01:24:22 +03:00
# include <linux/property.h>
2020-05-13 01:24:21 +03:00
# include <linux/regmap.h>
2016-10-01 15:19:00 +03:00
# include <linux/reset.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/usb/of.h>
# define REG_CONFIG 0x00
# define REG_CONFIG_CLK_EN BIT(0)
# define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1)
# define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4)
# define REG_CONFIG_CLK_32k_ALTSEL BIT(15)
# define REG_CONFIG_TEST_TRIG BIT(31)
# define REG_CTRL 0x04
# define REG_CTRL_SOFT_PRST BIT(0)
# define REG_CTRL_SOFT_HRESET BIT(1)
# define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2)
# define REG_CTRL_CLK_DET_RST BIT(4)
# define REG_CTRL_INTR_SEL BIT(5)
# define REG_CTRL_CLK_DETECTED BIT(8)
# define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9)
# define REG_CTRL_SOF_TOGGLE_OUT BIT(10)
# define REG_CTRL_POWER_ON_RESET BIT(15)
# define REG_CTRL_SLEEPM BIT(16)
# define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17)
# define REG_CTRL_TX_BITSTUFF_ENN BIT(18)
# define REG_CTRL_COMMON_ON BIT(19)
# define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20)
# define REG_CTRL_REF_CLK_SEL_SHIFT 20
# define REG_CTRL_FSEL_MASK GENMASK(24, 22)
# define REG_CTRL_FSEL_SHIFT 22
# define REG_CTRL_PORT_RESET BIT(25)
# define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26)
# define REG_ENDP_INTR 0x08
/* bits [31:26], [24:21] and [15:3] seem to be read-only */
# define REG_ADP_BC 0x0c
# define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0)
# define REG_ADP_BC_VBUS_VLD_EXT BIT(1)
# define REG_ADP_BC_OTG_DISABLE BIT(2)
# define REG_ADP_BC_ID_PULLUP BIT(3)
# define REG_ADP_BC_DRV_VBUS BIT(4)
# define REG_ADP_BC_ADP_PRB_EN BIT(5)
# define REG_ADP_BC_ADP_DISCHARGE BIT(6)
# define REG_ADP_BC_ADP_CHARGE BIT(7)
# define REG_ADP_BC_SESS_END BIT(8)
# define REG_ADP_BC_DEVICE_SESS_VLD BIT(9)
# define REG_ADP_BC_B_VALID BIT(10)
# define REG_ADP_BC_A_VALID BIT(11)
# define REG_ADP_BC_ID_DIG BIT(12)
# define REG_ADP_BC_VBUS_VALID BIT(13)
# define REG_ADP_BC_ADP_PROBE BIT(14)
# define REG_ADP_BC_ADP_SENSE BIT(15)
# define REG_ADP_BC_ACA_ENABLE BIT(16)
# define REG_ADP_BC_DCD_ENABLE BIT(17)
# define REG_ADP_BC_VDAT_DET_EN_B BIT(18)
# define REG_ADP_BC_VDAT_SRC_EN_B BIT(19)
# define REG_ADP_BC_CHARGE_SEL BIT(20)
# define REG_ADP_BC_CHARGE_DETECT BIT(21)
# define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22)
# define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23)
# define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24)
# define REG_ADP_BC_ACA_PIN_GND BIT(25)
# define REG_ADP_BC_ACA_PIN_FLOAT BIT(26)
2017-03-04 13:20:11 +03:00
# define REG_DBG_UART 0x10
2020-05-13 01:24:23 +03:00
# define REG_DBG_UART_BYPASS_SEL BIT(0)
# define REG_DBG_UART_BYPASS_DM_EN BIT(1)
# define REG_DBG_UART_BYPASS_DP_EN BIT(2)
# define REG_DBG_UART_BYPASS_DM_DATA BIT(3)
# define REG_DBG_UART_BYPASS_DP_DATA BIT(4)
# define REG_DBG_UART_FSV_MINUS BIT(5)
# define REG_DBG_UART_FSV_PLUS BIT(6)
# define REG_DBG_UART_FSV_BURN_IN_TEST BIT(7)
# define REG_DBG_UART_LOOPBACK_EN_B BIT(8)
# define REG_DBG_UART_SET_IDDQ BIT(9)
# define REG_DBG_UART_ATE_RESET BIT(10)
2016-10-01 15:19:00 +03:00
2017-03-04 13:20:11 +03:00
# define REG_TEST 0x14
2016-10-01 15:19:00 +03:00
# define REG_TEST_DATA_IN_MASK GENMASK(3, 0)
# define REG_TEST_EN_MASK GENMASK(7, 4)
# define REG_TEST_ADDR_MASK GENMASK(11, 8)
# define REG_TEST_DATA_OUT_SEL BIT(12)
# define REG_TEST_CLK BIT(13)
# define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14)
# define REG_TEST_DATA_OUT_MASK GENMASK(19, 16)
# define REG_TEST_DISABLE_ID_PULLUP BIT(20)
2017-03-04 13:20:11 +03:00
# define REG_TUNE 0x18
2016-10-01 15:19:00 +03:00
# define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0)
# define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2)
# define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4)
# define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8)
# define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10)
# define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11)
# define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13)
# define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17)
# define REG_TUNE_OTG_TUNE GENMASK(22, 20)
# define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23)
# define REG_TUNE_HOST_DM_PULLDOWN BIT(26)
# define REG_TUNE_HOST_DP_PULLDOWN BIT(27)
# define RESET_COMPLETE_TIME 500
# define ACA_ENABLE_COMPLETE_TIME 50
2020-05-13 01:24:22 +03:00
struct phy_meson8b_usb2_match_data {
bool host_enable_aca ;
} ;
2016-10-01 15:19:00 +03:00
struct phy_meson8b_usb2_priv {
2020-05-13 01:24:22 +03:00
struct regmap * regmap ;
enum usb_dr_mode dr_mode ;
struct clk * clk_usb_general ;
struct clk * clk_usb ;
struct reset_control * reset ;
const struct phy_meson8b_usb2_match_data * match ;
2016-10-01 15:19:00 +03:00
} ;
2020-05-13 01:24:21 +03:00
static const struct regmap_config phy_meson8b_usb2_regmap_conf = {
. reg_bits = 8 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. max_register = REG_TUNE ,
} ;
2016-10-01 15:19:00 +03:00
static int phy_meson8b_usb2_power_on ( struct phy * phy )
{
struct phy_meson8b_usb2_priv * priv = phy_get_drvdata ( phy ) ;
2020-05-13 01:24:21 +03:00
u32 reg ;
2016-10-01 15:19:00 +03:00
int ret ;
if ( ! IS_ERR_OR_NULL ( priv - > reset ) ) {
ret = reset_control_reset ( priv - > reset ) ;
if ( ret ) {
dev_err ( & phy - > dev , " Failed to trigger USB reset \n " ) ;
return ret ;
}
}
ret = clk_prepare_enable ( priv - > clk_usb_general ) ;
if ( ret ) {
dev_err ( & phy - > dev , " Failed to enable USB general clock \n " ) ;
return ret ;
}
ret = clk_prepare_enable ( priv - > clk_usb ) ;
if ( ret ) {
dev_err ( & phy - > dev , " Failed to enable USB DDR clock \n " ) ;
2016-10-22 17:33:42 +03:00
clk_disable_unprepare ( priv - > clk_usb_general ) ;
2016-10-01 15:19:00 +03:00
return ret ;
}
2020-05-13 01:24:21 +03:00
regmap_update_bits ( priv - > regmap , REG_CONFIG , REG_CONFIG_CLK_32k_ALTSEL ,
REG_CONFIG_CLK_32k_ALTSEL ) ;
2016-10-01 15:19:00 +03:00
2020-05-13 01:24:21 +03:00
regmap_update_bits ( priv - > regmap , REG_CTRL , REG_CTRL_REF_CLK_SEL_MASK ,
0x2 < < REG_CTRL_REF_CLK_SEL_SHIFT ) ;
2016-10-01 15:19:00 +03:00
2020-05-13 01:24:21 +03:00
regmap_update_bits ( priv - > regmap , REG_CTRL , REG_CTRL_FSEL_MASK ,
0x5 < < REG_CTRL_FSEL_SHIFT ) ;
2016-10-01 15:19:00 +03:00
/* reset the PHY */
2020-05-13 01:24:21 +03:00
regmap_update_bits ( priv - > regmap , REG_CTRL , REG_CTRL_POWER_ON_RESET ,
REG_CTRL_POWER_ON_RESET ) ;
2016-10-01 15:19:00 +03:00
udelay ( RESET_COMPLETE_TIME ) ;
2020-05-13 01:24:21 +03:00
regmap_update_bits ( priv - > regmap , REG_CTRL , REG_CTRL_POWER_ON_RESET , 0 ) ;
2016-10-01 15:19:00 +03:00
udelay ( RESET_COMPLETE_TIME ) ;
2020-05-13 01:24:21 +03:00
regmap_update_bits ( priv - > regmap , REG_CTRL , REG_CTRL_SOF_TOGGLE_OUT ,
REG_CTRL_SOF_TOGGLE_OUT ) ;
2016-10-01 15:19:00 +03:00
2020-05-13 01:24:23 +03:00
if ( priv - > dr_mode = = USB_DR_MODE_HOST ) {
regmap_update_bits ( priv - > regmap , REG_DBG_UART ,
REG_DBG_UART_SET_IDDQ , 0 ) ;
2016-10-01 15:19:00 +03:00
2020-05-13 01:24:23 +03:00
if ( priv - > match - > host_enable_aca ) {
regmap_update_bits ( priv - > regmap , REG_ADP_BC ,
REG_ADP_BC_ACA_ENABLE ,
REG_ADP_BC_ACA_ENABLE ) ;
2016-10-01 15:19:00 +03:00
2020-05-13 01:24:23 +03:00
udelay ( ACA_ENABLE_COMPLETE_TIME ) ;
regmap_read ( priv - > regmap , REG_ADP_BC , & reg ) ;
if ( reg & REG_ADP_BC_ACA_PIN_FLOAT ) {
dev_warn ( & phy - > dev , " USB ID detect failed! \n " ) ;
clk_disable_unprepare ( priv - > clk_usb ) ;
clk_disable_unprepare ( priv - > clk_usb_general ) ;
return - EINVAL ;
}
2016-10-01 15:19:00 +03:00
}
}
return 0 ;
}
static int phy_meson8b_usb2_power_off ( struct phy * phy )
{
struct phy_meson8b_usb2_priv * priv = phy_get_drvdata ( phy ) ;
2020-05-13 01:24:23 +03:00
if ( priv - > dr_mode = = USB_DR_MODE_HOST )
regmap_update_bits ( priv - > regmap , REG_DBG_UART ,
REG_DBG_UART_SET_IDDQ ,
REG_DBG_UART_SET_IDDQ ) ;
2016-10-01 15:19:00 +03:00
clk_disable_unprepare ( priv - > clk_usb ) ;
clk_disable_unprepare ( priv - > clk_usb_general ) ;
2021-08-17 07:15:39 +03:00
/* power off the PHY by putting it into reset mode */
regmap_update_bits ( priv - > regmap , REG_CTRL , REG_CTRL_POWER_ON_RESET ,
REG_CTRL_POWER_ON_RESET ) ;
2016-10-01 15:19:00 +03:00
return 0 ;
}
static const struct phy_ops phy_meson8b_usb2_ops = {
. power_on = phy_meson8b_usb2_power_on ,
. power_off = phy_meson8b_usb2_power_off ,
. owner = THIS_MODULE ,
} ;
static int phy_meson8b_usb2_probe ( struct platform_device * pdev )
{
struct phy_meson8b_usb2_priv * priv ;
struct phy * phy ;
struct phy_provider * phy_provider ;
2020-05-13 01:24:21 +03:00
void __iomem * base ;
2016-10-01 15:19:00 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2020-05-13 01:24:21 +03:00
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2020-05-13 01:24:22 +03:00
priv - > match = device_get_match_data ( & pdev - > dev ) ;
if ( ! priv - > match )
return - ENODEV ;
2020-05-13 01:24:21 +03:00
priv - > regmap = devm_regmap_init_mmio ( & pdev - > dev , base ,
& phy_meson8b_usb2_regmap_conf ) ;
if ( IS_ERR ( priv - > regmap ) )
return PTR_ERR ( priv - > regmap ) ;
2016-10-01 15:19:00 +03:00
priv - > clk_usb_general = devm_clk_get ( & pdev - > dev , " usb_general " ) ;
if ( IS_ERR ( priv - > clk_usb_general ) )
return PTR_ERR ( priv - > clk_usb_general ) ;
priv - > clk_usb = devm_clk_get ( & pdev - > dev , " usb " ) ;
if ( IS_ERR ( priv - > clk_usb ) )
return PTR_ERR ( priv - > clk_usb ) ;
2016-11-12 16:13:04 +03:00
priv - > reset = devm_reset_control_get_optional_shared ( & pdev - > dev , NULL ) ;
2016-10-01 15:19:00 +03:00
if ( PTR_ERR ( priv - > reset ) = = - EPROBE_DEFER )
return PTR_ERR ( priv - > reset ) ;
priv - > dr_mode = of_usb_get_dr_mode_by_phy ( pdev - > dev . of_node , - 1 ) ;
if ( priv - > dr_mode = = USB_DR_MODE_UNKNOWN ) {
dev_err ( & pdev - > dev ,
" missing dual role configuration of the controller \n " ) ;
return - EINVAL ;
}
phy = devm_phy_create ( & pdev - > dev , NULL , & phy_meson8b_usb2_ops ) ;
if ( IS_ERR ( phy ) ) {
2021-08-17 07:15:40 +03:00
return dev_err_probe ( & pdev - > dev , PTR_ERR ( phy ) ,
" failed to create PHY \n " ) ;
2016-10-01 15:19:00 +03:00
}
phy_set_drvdata ( phy , priv ) ;
phy_provider =
devm_of_phy_provider_register ( & pdev - > dev , of_phy_simple_xlate ) ;
return PTR_ERR_OR_ZERO ( phy_provider ) ;
}
2020-05-13 01:24:22 +03:00
static const struct phy_meson8b_usb2_match_data phy_meson8_usb2_match_data = {
. host_enable_aca = false ,
} ;
static const struct phy_meson8b_usb2_match_data phy_meson8b_usb2_match_data = {
. host_enable_aca = true ,
} ;
2016-10-01 15:19:00 +03:00
static const struct of_device_id phy_meson8b_usb2_of_match [ ] = {
2020-05-13 01:24:22 +03:00
{
. compatible = " amlogic,meson8-usb2-phy " ,
. data = & phy_meson8_usb2_match_data
} ,
{
. compatible = " amlogic,meson8b-usb2-phy " ,
. data = & phy_meson8b_usb2_match_data
} ,
2020-05-13 01:24:24 +03:00
{
. compatible = " amlogic,meson8m2-usb2-phy " ,
. data = & phy_meson8b_usb2_match_data
} ,
2020-05-13 01:24:22 +03:00
{
. compatible = " amlogic,meson-gxbb-usb2-phy " ,
. data = & phy_meson8b_usb2_match_data
} ,
{ /* sentinel */ }
2016-10-01 15:19:00 +03:00
} ;
MODULE_DEVICE_TABLE ( of , phy_meson8b_usb2_of_match ) ;
static struct platform_driver phy_meson8b_usb2_driver = {
. probe = phy_meson8b_usb2_probe ,
. driver = {
. name = " phy-meson-usb2 " ,
. of_match_table = phy_meson8b_usb2_of_match ,
} ,
} ;
module_platform_driver ( phy_meson8b_usb2_driver ) ;
MODULE_AUTHOR ( " Martin Blumenstingl <martin.blumenstingl@googlemail.com> " ) ;
2020-05-13 01:24:24 +03:00
MODULE_DESCRIPTION ( " Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver " ) ;
2016-10-01 15:19:00 +03:00
MODULE_LICENSE ( " GPL " ) ;