2018-11-15 15:12:47 +01:00
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2017 NXP. */
2020-08-24 21:33:34 +08:00
# include <linux/bitfield.h>
2018-11-15 15:12:47 +01:00
# include <linux/clk.h>
2020-08-24 21:33:34 +08:00
# include <linux/delay.h>
2018-11-15 15:12:47 +01:00
# include <linux/io.h>
# include <linux/module.h>
2020-08-24 21:33:34 +08:00
# include <linux/of_platform.h>
2018-11-15 15:12:47 +01:00
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
2019-04-04 18:41:48 +02:00
# include <linux/regulator/consumer.h>
2018-11-15 15:12:47 +01:00
# define PHY_CTRL0 0x0
# define PHY_CTRL0_REF_SSP_EN BIT(2)
2020-08-24 21:33:34 +08:00
# define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
# define PHY_CTRL0_FSEL_24M 0x2a
2018-11-15 15:12:47 +01:00
# define PHY_CTRL1 0x4
# define PHY_CTRL1_RESET BIT(0)
# define PHY_CTRL1_COMMONONN BIT(1)
# define PHY_CTRL1_ATERESET BIT(3)
# define PHY_CTRL1_VDATSRCENB0 BIT(19)
# define PHY_CTRL1_VDATDETENB0 BIT(20)
# define PHY_CTRL2 0x8
# define PHY_CTRL2_TXENABLEN0 BIT(8)
2020-08-24 21:33:34 +08:00
# define PHY_CTRL2_OTG_DISABLE BIT(9)
# define PHY_CTRL6 0x18
# define PHY_CTRL6_ALT_CLK_EN BIT(1)
# define PHY_CTRL6_ALT_CLK_SEL BIT(0)
2018-11-15 15:12:47 +01:00
struct imx8mq_usb_phy {
struct phy * phy ;
struct clk * clk ;
void __iomem * base ;
2019-04-04 18:41:48 +02:00
struct regulator * vbus ;
2018-11-15 15:12:47 +01:00
} ;
static int imx8mq_usb_phy_init ( struct phy * phy )
{
struct imx8mq_usb_phy * imx_phy = phy_get_drvdata ( phy ) ;
u32 value ;
value = readl ( imx_phy - > base + PHY_CTRL1 ) ;
value & = ~ ( PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |
PHY_CTRL1_COMMONONN ) ;
value | = PHY_CTRL1_RESET | PHY_CTRL1_ATERESET ;
writel ( value , imx_phy - > base + PHY_CTRL1 ) ;
value = readl ( imx_phy - > base + PHY_CTRL0 ) ;
value | = PHY_CTRL0_REF_SSP_EN ;
writel ( value , imx_phy - > base + PHY_CTRL0 ) ;
value = readl ( imx_phy - > base + PHY_CTRL2 ) ;
value | = PHY_CTRL2_TXENABLEN0 ;
writel ( value , imx_phy - > base + PHY_CTRL2 ) ;
value = readl ( imx_phy - > base + PHY_CTRL1 ) ;
value & = ~ ( PHY_CTRL1_RESET | PHY_CTRL1_ATERESET ) ;
writel ( value , imx_phy - > base + PHY_CTRL1 ) ;
return 0 ;
}
2020-08-24 21:33:34 +08:00
static int imx8mp_usb_phy_init ( struct phy * phy )
{
struct imx8mq_usb_phy * imx_phy = phy_get_drvdata ( phy ) ;
u32 value ;
/* USB3.0 PHY signal fsel for 24M ref */
value = readl ( imx_phy - > base + PHY_CTRL0 ) ;
value & = ~ PHY_CTRL0_FSEL_MASK ;
value | = FIELD_PREP ( PHY_CTRL0_FSEL_MASK , PHY_CTRL0_FSEL_24M ) ;
writel ( value , imx_phy - > base + PHY_CTRL0 ) ;
/* Disable alt_clk_en and use internal MPLL clocks */
value = readl ( imx_phy - > base + PHY_CTRL6 ) ;
value & = ~ ( PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN ) ;
writel ( value , imx_phy - > base + PHY_CTRL6 ) ;
value = readl ( imx_phy - > base + PHY_CTRL1 ) ;
value & = ~ ( PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 ) ;
value | = PHY_CTRL1_RESET | PHY_CTRL1_ATERESET ;
writel ( value , imx_phy - > base + PHY_CTRL1 ) ;
value = readl ( imx_phy - > base + PHY_CTRL0 ) ;
value | = PHY_CTRL0_REF_SSP_EN ;
writel ( value , imx_phy - > base + PHY_CTRL0 ) ;
value = readl ( imx_phy - > base + PHY_CTRL2 ) ;
value | = PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE ;
writel ( value , imx_phy - > base + PHY_CTRL2 ) ;
udelay ( 10 ) ;
value = readl ( imx_phy - > base + PHY_CTRL1 ) ;
value & = ~ ( PHY_CTRL1_RESET | PHY_CTRL1_ATERESET ) ;
writel ( value , imx_phy - > base + PHY_CTRL1 ) ;
return 0 ;
}
2018-11-15 15:12:47 +01:00
static int imx8mq_phy_power_on ( struct phy * phy )
{
struct imx8mq_usb_phy * imx_phy = phy_get_drvdata ( phy ) ;
2019-04-04 18:41:48 +02:00
int ret ;
ret = regulator_enable ( imx_phy - > vbus ) ;
if ( ret )
return ret ;
2018-11-15 15:12:47 +01:00
return clk_prepare_enable ( imx_phy - > clk ) ;
}
static int imx8mq_phy_power_off ( struct phy * phy )
{
struct imx8mq_usb_phy * imx_phy = phy_get_drvdata ( phy ) ;
clk_disable_unprepare ( imx_phy - > clk ) ;
2019-04-04 18:41:48 +02:00
regulator_disable ( imx_phy - > vbus ) ;
2018-11-15 15:12:47 +01:00
return 0 ;
}
2020-08-24 00:00:19 +02:00
static const struct phy_ops imx8mq_usb_phy_ops = {
2018-11-15 15:12:47 +01:00
. init = imx8mq_usb_phy_init ,
. power_on = imx8mq_phy_power_on ,
. power_off = imx8mq_phy_power_off ,
. owner = THIS_MODULE ,
} ;
2020-09-26 22:58:44 +02:00
static const struct phy_ops imx8mp_usb_phy_ops = {
2020-08-24 21:33:34 +08:00
. init = imx8mp_usb_phy_init ,
. power_on = imx8mq_phy_power_on ,
. power_off = imx8mq_phy_power_off ,
. owner = THIS_MODULE ,
} ;
static const struct of_device_id imx8mq_usb_phy_of_match [ ] = {
{ . compatible = " fsl,imx8mq-usb-phy " ,
. data = & imx8mq_usb_phy_ops , } ,
{ . compatible = " fsl,imx8mp-usb-phy " ,
. data = & imx8mp_usb_phy_ops , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , imx8mq_usb_phy_of_match ) ;
2018-11-15 15:12:47 +01:00
static int imx8mq_usb_phy_probe ( struct platform_device * pdev )
{
struct phy_provider * phy_provider ;
struct device * dev = & pdev - > dev ;
struct imx8mq_usb_phy * imx_phy ;
2020-08-24 21:33:34 +08:00
const struct phy_ops * phy_ops ;
2018-11-15 15:12:47 +01:00
imx_phy = devm_kzalloc ( dev , sizeof ( * imx_phy ) , GFP_KERNEL ) ;
if ( ! imx_phy )
return - ENOMEM ;
imx_phy - > clk = devm_clk_get ( dev , " phy " ) ;
if ( IS_ERR ( imx_phy - > clk ) ) {
dev_err ( dev , " failed to get imx8mq usb phy clock \n " ) ;
return PTR_ERR ( imx_phy - > clk ) ;
}
2020-11-06 14:08:38 +08:00
imx_phy - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2018-11-15 15:12:47 +01:00
if ( IS_ERR ( imx_phy - > base ) )
return PTR_ERR ( imx_phy - > base ) ;
2020-08-24 21:33:34 +08:00
phy_ops = of_device_get_match_data ( dev ) ;
if ( ! phy_ops )
return - EINVAL ;
imx_phy - > phy = devm_phy_create ( dev , NULL , phy_ops ) ;
2018-11-15 15:12:47 +01:00
if ( IS_ERR ( imx_phy - > phy ) )
return PTR_ERR ( imx_phy - > phy ) ;
2019-04-04 18:41:48 +02:00
imx_phy - > vbus = devm_regulator_get ( dev , " vbus " ) ;
if ( IS_ERR ( imx_phy - > vbus ) )
return PTR_ERR ( imx_phy - > vbus ) ;
2018-11-15 15:12:47 +01:00
phy_set_drvdata ( imx_phy - > phy , imx_phy ) ;
phy_provider = devm_of_phy_provider_register ( dev , of_phy_simple_xlate ) ;
return PTR_ERR_OR_ZERO ( phy_provider ) ;
}
static struct platform_driver imx8mq_usb_phy_driver = {
. probe = imx8mq_usb_phy_probe ,
. driver = {
. name = " imx8mq-usb-phy " ,
. of_match_table = imx8mq_usb_phy_of_match ,
}
} ;
module_platform_driver ( imx8mq_usb_phy_driver ) ;
MODULE_DESCRIPTION ( " FSL IMX8MQ USB PHY driver " ) ;
MODULE_LICENSE ( " GPL " ) ;