2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2021-03-29 17:03:18 +03:00
/*
2020-01-04 18:20:55 +03:00
* dwc3 - exynos . c - Samsung Exynos DWC3 Specific Glue layer
2012-02-15 12:04:56 +04:00
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Author : Anton Tikhomirov < av . tikhomirov @ samsung . com >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
2012-11-03 16:30:27 +04:00
# include <linux/of.h>
2013-03-14 16:39:49 +04:00
# include <linux/of_platform.h>
2014-04-21 16:16:44 +04:00
# include <linux/regulator/consumer.h>
2012-02-15 12:04:56 +04:00
2018-09-18 11:16:51 +03:00
# define DWC3_EXYNOS_MAX_CLOCKS 4
struct dwc3_exynos_driverdata {
const char * clk_names [ DWC3_EXYNOS_MAX_CLOCKS ] ;
int num_clks ;
int suspend_clk_idx ;
} ;
2012-02-15 12:04:56 +04:00
struct dwc3_exynos {
struct device * dev ;
2018-09-18 11:16:51 +03:00
const char * * clk_names ;
struct clk * clks [ DWC3_EXYNOS_MAX_CLOCKS ] ;
int num_clks ;
int suspend_clk_idx ;
2014-11-21 16:35:46 +03:00
2014-04-21 16:16:44 +04:00
struct regulator * vdd33 ;
struct regulator * vdd10 ;
2012-02-15 12:04:56 +04:00
} ;
2012-11-19 22:21:48 +04:00
static int dwc3_exynos_probe ( struct platform_device * pdev )
2012-02-15 12:04:56 +04:00
{
struct dwc3_exynos * exynos ;
2012-11-13 06:20:49 +04:00
struct device * dev = & pdev - > dev ;
2013-03-14 16:39:49 +04:00
struct device_node * node = dev - > of_node ;
2018-09-18 11:16:51 +03:00
const struct dwc3_exynos_driverdata * driver_data ;
int i , ret ;
2012-02-15 12:04:56 +04:00
2012-11-13 06:20:49 +04:00
exynos = devm_kzalloc ( dev , sizeof ( * exynos ) , GFP_KERNEL ) ;
2014-07-17 07:45:11 +04:00
if ( ! exynos )
2014-05-15 16:53:32 +04:00
return - ENOMEM ;
2012-02-15 12:04:56 +04:00
2018-09-18 11:16:51 +03:00
driver_data = of_device_get_match_data ( dev ) ;
exynos - > dev = dev ;
exynos - > num_clks = driver_data - > num_clks ;
exynos - > clk_names = ( const char * * ) driver_data - > clk_names ;
exynos - > suspend_clk_idx = driver_data - > suspend_clk_idx ;
2012-02-15 12:04:56 +04:00
2018-09-18 11:16:51 +03:00
platform_set_drvdata ( pdev , exynos ) ;
2014-11-21 16:35:45 +03:00
2018-09-18 11:16:51 +03:00
for ( i = 0 ; i < exynos - > num_clks ; i + + ) {
exynos - > clks [ i ] = devm_clk_get ( dev , exynos - > clk_names [ i ] ) ;
if ( IS_ERR ( exynos - > clks [ i ] ) ) {
dev_err ( dev , " failed to get clock: %s \n " ,
exynos - > clk_names [ i ] ) ;
return PTR_ERR ( exynos - > clks [ i ] ) ;
}
2012-02-15 12:04:56 +04:00
}
2018-09-18 11:16:51 +03:00
for ( i = 0 ; i < exynos - > num_clks ; i + + ) {
ret = clk_prepare_enable ( exynos - > clks [ i ] ) ;
if ( ret ) {
2019-01-22 00:23:50 +03:00
while ( i - - > 0 )
2018-09-18 11:16:51 +03:00
clk_disable_unprepare ( exynos - > clks [ i ] ) ;
return ret ;
2014-11-21 16:35:47 +03:00
}
}
2018-09-18 11:16:51 +03:00
if ( exynos - > suspend_clk_idx > = 0 )
clk_prepare_enable ( exynos - > clks [ exynos - > suspend_clk_idx ] ) ;
2014-04-21 16:16:44 +04:00
exynos - > vdd33 = devm_regulator_get ( dev , " vdd33 " ) ;
if ( IS_ERR ( exynos - > vdd33 ) ) {
ret = PTR_ERR ( exynos - > vdd33 ) ;
2017-01-30 22:25:04 +03:00
goto vdd33_err ;
2014-04-21 16:16:44 +04:00
}
ret = regulator_enable ( exynos - > vdd33 ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable VDD33 supply \n " ) ;
2017-01-30 22:25:04 +03:00
goto vdd33_err ;
2014-04-21 16:16:44 +04:00
}
exynos - > vdd10 = devm_regulator_get ( dev , " vdd10 " ) ;
if ( IS_ERR ( exynos - > vdd10 ) ) {
ret = PTR_ERR ( exynos - > vdd10 ) ;
2017-01-30 22:25:04 +03:00
goto vdd10_err ;
2014-04-21 16:16:44 +04:00
}
ret = regulator_enable ( exynos - > vdd10 ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable VDD10 supply \n " ) ;
2017-01-30 22:25:04 +03:00
goto vdd10_err ;
2014-04-21 16:16:44 +04:00
}
2013-03-14 16:39:49 +04:00
if ( node ) {
ret = of_platform_populate ( node , NULL , NULL , dev ) ;
if ( ret ) {
dev_err ( dev , " failed to add dwc3 core \n " ) ;
2017-01-30 22:25:04 +03:00
goto populate_err ;
2013-03-14 16:39:49 +04:00
}
} else {
dev_err ( dev , " no device node, failed to add dwc3 core \n " ) ;
ret = - ENODEV ;
2017-01-30 22:25:04 +03:00
goto populate_err ;
2012-02-15 12:04:56 +04:00
}
return 0 ;
2017-01-30 22:25:04 +03:00
populate_err :
2014-04-21 16:16:44 +04:00
regulator_disable ( exynos - > vdd10 ) ;
2017-01-30 22:25:04 +03:00
vdd10_err :
2014-04-21 16:16:44 +04:00
regulator_disable ( exynos - > vdd33 ) ;
2017-01-30 22:25:04 +03:00
vdd33_err :
2018-09-18 11:16:51 +03:00
for ( i = exynos - > num_clks - 1 ; i > = 0 ; i - - )
clk_disable_unprepare ( exynos - > clks [ i ] ) ;
if ( exynos - > suspend_clk_idx > = 0 )
clk_disable_unprepare ( exynos - > clks [ exynos - > suspend_clk_idx ] ) ;
2012-02-15 12:04:56 +04:00
return ret ;
}
2023-05-18 02:01:16 +03:00
static void dwc3_exynos_remove ( struct platform_device * pdev )
2012-02-15 12:04:56 +04:00
{
struct dwc3_exynos * exynos = platform_get_drvdata ( pdev ) ;
2018-09-18 11:16:51 +03:00
int i ;
2012-02-15 12:04:56 +04:00
2022-11-10 18:41:31 +03:00
of_platform_depopulate ( & pdev - > dev ) ;
2012-02-15 12:04:56 +04:00
2018-09-18 11:16:51 +03:00
for ( i = exynos - > num_clks - 1 ; i > = 0 ; i - - )
clk_disable_unprepare ( exynos - > clks [ i ] ) ;
if ( exynos - > suspend_clk_idx > = 0 )
clk_disable_unprepare ( exynos - > clks [ exynos - > suspend_clk_idx ] ) ;
2012-02-15 12:04:56 +04:00
2014-04-21 16:16:44 +04:00
regulator_disable ( exynos - > vdd33 ) ;
regulator_disable ( exynos - > vdd10 ) ;
2012-02-15 12:04:56 +04:00
}
2018-09-18 11:16:51 +03:00
static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
. clk_names = { " usbdrd30 " } ,
. num_clks = 1 ,
. suspend_clk_idx = - 1 ,
} ;
2018-09-18 11:16:52 +03:00
static const struct dwc3_exynos_driverdata exynos5433_drvdata = {
. clk_names = { " aclk " , " susp_clk " , " pipe_pclk " , " phyclk " } ,
. num_clks = 4 ,
. suspend_clk_idx = 1 ,
} ;
2018-09-18 11:16:51 +03:00
static const struct dwc3_exynos_driverdata exynos7_drvdata = {
. clk_names = { " usbdrd30 " , " usbdrd30_susp_clk " , " usbdrd30_axius_clk " } ,
. num_clks = 3 ,
. suspend_clk_idx = 1 ,
} ;
2023-08-19 06:17:26 +03:00
static const struct dwc3_exynos_driverdata exynos850_drvdata = {
. clk_names = { " bus_early " , " ref " } ,
. num_clks = 2 ,
. suspend_clk_idx = - 1 ,
} ;
2012-11-03 16:30:27 +04:00
static const struct of_device_id exynos_dwc3_match [ ] = {
2018-09-18 11:16:51 +03:00
{
. compatible = " samsung,exynos5250-dwusb3 " ,
. data = & exynos5250_drvdata ,
2018-09-18 11:16:52 +03:00
} , {
. compatible = " samsung,exynos5433-dwusb3 " ,
. data = & exynos5433_drvdata ,
2018-09-18 11:16:51 +03:00
} , {
. compatible = " samsung,exynos7-dwusb3 " ,
. data = & exynos7_drvdata ,
2023-08-19 06:17:26 +03:00
} , {
. compatible = " samsung,exynos850-dwusb3 " ,
. data = & exynos850_drvdata ,
2018-09-18 11:16:51 +03:00
} , {
}
2012-11-03 16:30:27 +04:00
} ;
MODULE_DEVICE_TABLE ( of , exynos_dwc3_match ) ;
2013-03-26 05:52:48 +04:00
# ifdef CONFIG_PM_SLEEP
2012-10-16 13:45:38 +04:00
static int dwc3_exynos_suspend ( struct device * dev )
{
struct dwc3_exynos * exynos = dev_get_drvdata ( dev ) ;
2018-09-18 11:16:51 +03:00
int i ;
2012-10-16 13:45:38 +04:00
2018-09-18 11:16:51 +03:00
for ( i = exynos - > num_clks - 1 ; i > = 0 ; i - - )
clk_disable_unprepare ( exynos - > clks [ i ] ) ;
2012-10-16 13:45:38 +04:00
2014-04-21 16:16:44 +04:00
regulator_disable ( exynos - > vdd33 ) ;
regulator_disable ( exynos - > vdd10 ) ;
2012-10-16 13:45:38 +04:00
return 0 ;
}
static int dwc3_exynos_resume ( struct device * dev )
{
struct dwc3_exynos * exynos = dev_get_drvdata ( dev ) ;
2018-09-18 11:16:51 +03:00
int i , ret ;
2014-04-21 16:16:44 +04:00
ret = regulator_enable ( exynos - > vdd33 ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable VDD33 supply \n " ) ;
return ret ;
}
ret = regulator_enable ( exynos - > vdd10 ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable VDD10 supply \n " ) ;
return ret ;
}
2012-10-16 13:45:38 +04:00
2018-09-18 11:16:51 +03:00
for ( i = 0 ; i < exynos - > num_clks ; i + + ) {
ret = clk_prepare_enable ( exynos - > clks [ i ] ) ;
if ( ret ) {
2019-01-22 00:23:50 +03:00
while ( i - - > 0 )
2018-09-18 11:16:51 +03:00
clk_disable_unprepare ( exynos - > clks [ i ] ) ;
return ret ;
}
}
2012-10-16 13:45:38 +04:00
return 0 ;
}
static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( dwc3_exynos_suspend , dwc3_exynos_resume )
} ;
# define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
# else
# define DEV_PM_OPS NULL
2013-03-26 05:52:48 +04:00
# endif /* CONFIG_PM_SLEEP */
2012-10-16 13:45:38 +04:00
2012-02-15 12:04:56 +04:00
static struct platform_driver dwc3_exynos_driver = {
. probe = dwc3_exynos_probe ,
2023-05-18 02:01:16 +03:00
. remove_new = dwc3_exynos_remove ,
2012-02-15 12:04:56 +04:00
. driver = {
. name = " exynos-dwc3 " ,
2014-11-04 05:01:47 +03:00
. of_match_table = exynos_dwc3_match ,
2012-10-16 13:45:38 +04:00
. pm = DEV_PM_OPS ,
2012-02-15 12:04:56 +04:00
} ,
} ;
module_platform_driver ( dwc3_exynos_driver ) ;
MODULE_AUTHOR ( " Anton Tikhomirov <av.tikhomirov@samsung.com> " ) ;
2013-06-30 15:15:11 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2020-01-04 18:20:55 +03:00
MODULE_DESCRIPTION ( " DesignWare USB3 Exynos Glue Layer " ) ;