2018-11-15 15:12:47 +01:00
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2017 NXP. */
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/module.h>
# 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)
# 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)
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 ;
}
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 ;
}
static struct phy_ops imx8mq_usb_phy_ops = {
. init = imx8mq_usb_phy_init ,
. power_on = imx8mq_phy_power_on ,
. power_off = imx8mq_phy_power_off ,
. owner = THIS_MODULE ,
} ;
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 ;
struct resource * res ;
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 ) ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
imx_phy - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( imx_phy - > base ) )
return PTR_ERR ( imx_phy - > base ) ;
imx_phy - > phy = devm_phy_create ( dev , NULL , & imx8mq_usb_phy_ops ) ;
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 const struct of_device_id imx8mq_usb_phy_of_match [ ] = {
{ . compatible = " fsl,imx8mq-usb-phy " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , imx8mq_usb_phy_of_match ) ;
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 " ) ;