2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2011-12-23 11:20:54 +09:00
/*
* SAMSUNG EXYNOS USB HOST OHCI Controller
*
* Copyright ( C ) 2011 Samsung Electronics Co . Ltd
* Author : Jingoo Han < jg1 . han @ samsung . com >
*/
# include <linux/clk.h>
2013-09-21 16:38:38 +05:30
# include <linux/dma-mapping.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
2012-07-16 11:25:36 +05:30
# include <linux/of.h>
2011-12-23 11:20:54 +09:00
# include <linux/platform_device.h>
2014-05-05 10:32:57 +05:30
# include <linux/phy/phy.h>
2013-09-21 16:38:38 +05:30
# include <linux/usb.h>
# include <linux/usb/hcd.h>
# include "ohci.h"
2020-01-04 16:20:55 +01:00
# define DRIVER_DESC "OHCI Exynos driver"
2013-09-21 16:38:38 +05:30
static const char hcd_name [ ] = " ohci-exynos " ;
static struct hc_driver __read_mostly exynos_ohci_hc_driver ;
# define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
2011-12-23 11:20:54 +09:00
2014-05-05 10:32:57 +05:30
# define PHY_NUMBER 3
2011-12-23 11:20:54 +09:00
struct exynos_ohci_hcd {
struct clk * clk ;
2019-05-20 11:08:23 +02:00
struct device_node * of_node ;
2014-09-22 11:16:19 +05:30
struct phy * phy [ PHY_NUMBER ] ;
2019-07-26 10:14:52 +02:00
bool legacy_phy ;
2011-12-23 11:20:54 +09:00
} ;
2014-05-05 10:32:57 +05:30
static int exynos_ohci_get_phy ( struct device * dev ,
struct exynos_ohci_hcd * exynos_ohci )
{
struct device_node * child ;
struct phy * phy ;
2019-07-26 10:14:52 +02:00
int phy_number , num_phys ;
2014-09-22 11:16:19 +05:30
int ret ;
2014-05-05 10:32:57 +05:30
2014-09-22 11:16:19 +05:30
/* Get PHYs for the controller */
2019-07-26 10:14:52 +02:00
num_phys = of_count_phandle_with_args ( dev - > of_node , " phys " ,
" #phy-cells " ) ;
for ( phy_number = 0 ; phy_number < num_phys ; phy_number + + ) {
phy = devm_of_phy_get_by_index ( dev , dev - > of_node , phy_number ) ;
if ( IS_ERR ( phy ) )
return PTR_ERR ( phy ) ;
exynos_ohci - > phy [ phy_number ] = phy ;
}
if ( num_phys > 0 )
return 0 ;
/* Get PHYs using legacy bindings */
2014-05-05 10:32:57 +05:30
for_each_available_child_of_node ( dev - > of_node , child ) {
ret = of_property_read_u32 ( child , " reg " , & phy_number ) ;
if ( ret ) {
dev_err ( dev , " Failed to parse device tree \n " ) ;
of_node_put ( child ) ;
return ret ;
}
if ( phy_number > = PHY_NUMBER ) {
dev_err ( dev , " Invalid number of PHYs \n " ) ;
of_node_put ( child ) ;
return - EINVAL ;
}
2014-06-06 14:13:44 +05:30
phy = devm_of_phy_get ( dev , child , NULL ) ;
2014-09-22 11:16:19 +05:30
exynos_ohci - > phy [ phy_number ] = phy ;
if ( IS_ERR ( phy ) ) {
ret = PTR_ERR ( phy ) ;
if ( ret = = - EPROBE_DEFER ) {
2017-01-07 10:41:41 +02:00
of_node_put ( child ) ;
2014-09-22 11:16:19 +05:30
return ret ;
} else if ( ret ! = - ENOSYS & & ret ! = - ENODEV ) {
dev_err ( dev ,
" Error retrieving usb2 phy: %d \n " , ret ) ;
2017-01-07 10:41:41 +02:00
of_node_put ( child ) ;
2014-09-22 11:16:19 +05:30
return ret ;
}
2014-08-05 16:09:08 +05:30
}
2014-05-05 10:32:57 +05:30
}
2019-07-26 10:14:52 +02:00
exynos_ohci - > legacy_phy = true ;
2014-09-22 11:16:19 +05:30
return 0 ;
2014-05-05 10:32:57 +05:30
}
static int exynos_ohci_phy_enable ( struct device * dev )
2013-01-22 18:30:43 +05:30
{
2014-05-05 10:33:42 +05:30
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2013-09-21 16:38:38 +05:30
struct exynos_ohci_hcd * exynos_ohci = to_exynos_ohci ( hcd ) ;
2014-05-05 10:32:57 +05:30
int i ;
int ret = 0 ;
for ( i = 0 ; ret = = 0 & & i < PHY_NUMBER ; i + + )
2014-09-22 11:16:19 +05:30
if ( ! IS_ERR ( exynos_ohci - > phy [ i ] ) )
ret = phy_power_on ( exynos_ohci - > phy [ i ] ) ;
2014-05-05 10:32:57 +05:30
if ( ret )
for ( i - - ; i > = 0 ; i - - )
2014-09-22 11:16:19 +05:30
if ( ! IS_ERR ( exynos_ohci - > phy [ i ] ) )
phy_power_off ( exynos_ohci - > phy [ i ] ) ;
2014-05-05 10:32:57 +05:30
return ret ;
2013-01-22 18:30:43 +05:30
}
2014-05-05 10:33:42 +05:30
static void exynos_ohci_phy_disable ( struct device * dev )
2013-01-22 18:30:43 +05:30
{
2014-05-05 10:33:42 +05:30
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2013-09-21 16:38:38 +05:30
struct exynos_ohci_hcd * exynos_ohci = to_exynos_ohci ( hcd ) ;
2014-05-05 10:32:57 +05:30
int i ;
2013-01-22 18:30:43 +05:30
2014-05-05 10:32:57 +05:30
for ( i = 0 ; i < PHY_NUMBER ; i + + )
2014-09-22 11:16:19 +05:30
if ( ! IS_ERR ( exynos_ohci - > phy [ i ] ) )
phy_power_off ( exynos_ohci - > phy [ i ] ) ;
2013-01-22 18:30:43 +05:30
}
2012-11-19 13:21:48 -05:00
static int exynos_ohci_probe ( struct platform_device * pdev )
2011-12-23 11:20:54 +09:00
{
struct exynos_ohci_hcd * exynos_ohci ;
struct usb_hcd * hcd ;
struct resource * res ;
int irq ;
int err ;
2012-07-16 11:25:36 +05:30
/*
* Right now device - tree probed devices don ' t get dma_mask set .
* Since shared usb code relies on it , set it here for now .
* Once we move to full device tree support this will vanish off .
*/
2013-06-27 12:36:37 +01:00
err = dma_coerce_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
2013-06-10 16:28:49 +01:00
if ( err )
return err ;
2012-07-16 11:25:36 +05:30
2013-09-21 16:38:38 +05:30
hcd = usb_create_hcd ( & exynos_ohci_hc_driver ,
& pdev - > dev , dev_name ( & pdev - > dev ) ) ;
if ( ! hcd ) {
dev_err ( & pdev - > dev , " Unable to create HCD \n " ) ;
2011-12-23 11:20:54 +09:00
return - ENOMEM ;
2013-09-21 16:38:38 +05:30
}
exynos_ohci = to_exynos_ohci ( hcd ) ;
2011-12-23 11:20:54 +09:00
2014-05-05 10:32:57 +05:30
err = exynos_ohci_get_phy ( & pdev - > dev , exynos_ohci ) ;
if ( err )
goto fail_clk ;
2013-01-22 18:30:43 +05:30
2012-10-04 16:11:50 +09:00
exynos_ohci - > clk = devm_clk_get ( & pdev - > dev , " usbhost " ) ;
2011-12-23 11:20:54 +09:00
if ( IS_ERR ( exynos_ohci - > clk ) ) {
dev_err ( & pdev - > dev , " Failed to get usbhost clock \n " ) ;
err = PTR_ERR ( exynos_ohci - > clk ) ;
goto fail_clk ;
}
2012-10-03 08:41:37 +09:00
err = clk_prepare_enable ( exynos_ohci - > clk ) ;
2011-12-23 11:20:54 +09:00
if ( err )
2012-10-04 16:11:50 +09:00
goto fail_clk ;
2011-12-23 11:20:54 +09:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2014-05-10 17:30:10 +05:30
hcd - > regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( hcd - > regs ) ) {
err = PTR_ERR ( hcd - > regs ) ;
2011-12-23 11:20:54 +09:00
goto fail_io ;
}
2014-11-04 07:51:13 +05:30
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2011-12-23 11:20:54 +09:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " Failed to get IRQ \n " ) ;
err = - ENODEV ;
2012-06-28 16:30:30 +09:00
goto fail_io ;
2011-12-23 11:20:54 +09:00
}
2013-09-21 16:38:38 +05:30
platform_set_drvdata ( pdev , hcd ) ;
2011-12-23 11:20:54 +09:00
2014-05-05 10:32:57 +05:30
err = exynos_ohci_phy_enable ( & pdev - > dev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Failed to enable USB phy \n " ) ;
goto fail_io ;
}
2011-12-23 11:20:54 +09:00
2019-05-20 11:08:23 +02:00
/*
2019-07-26 10:14:52 +02:00
* Workaround : reset of_node pointer to avoid conflict between legacy
* Exynos OHCI port subnodes and generic USB device bindings
2019-05-20 11:08:23 +02:00
*/
exynos_ohci - > of_node = pdev - > dev . of_node ;
2019-07-26 10:14:52 +02:00
if ( exynos_ohci - > legacy_phy )
pdev - > dev . of_node = NULL ;
2019-05-20 11:08:23 +02:00
2011-12-23 11:20:54 +09:00
err = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Failed to add USB HCD \n " ) ;
2013-01-22 18:30:43 +05:30
goto fail_add_hcd ;
2011-12-23 11:20:54 +09:00
}
2013-11-05 10:46:02 +08:00
device_wakeup_enable ( hcd - > self . controller ) ;
2011-12-23 11:20:54 +09:00
return 0 ;
2013-01-22 18:30:43 +05:30
fail_add_hcd :
2014-05-05 10:33:42 +05:30
exynos_ohci_phy_disable ( & pdev - > dev ) ;
2019-05-20 11:08:23 +02:00
pdev - > dev . of_node = exynos_ohci - > of_node ;
2011-12-23 11:20:54 +09:00
fail_io :
2012-10-03 08:41:37 +09:00
clk_disable_unprepare ( exynos_ohci - > clk ) ;
2011-12-23 11:20:54 +09:00
fail_clk :
usb_put_hcd ( hcd ) ;
return err ;
}
2012-11-19 13:26:20 -05:00
static int exynos_ohci_remove ( struct platform_device * pdev )
2011-12-23 11:20:54 +09:00
{
2013-09-21 16:38:38 +05:30
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
struct exynos_ohci_hcd * exynos_ohci = to_exynos_ohci ( hcd ) ;
2011-12-23 11:20:54 +09:00
2019-05-20 11:08:23 +02:00
pdev - > dev . of_node = exynos_ohci - > of_node ;
2011-12-23 11:20:54 +09:00
usb_remove_hcd ( hcd ) ;
2014-05-05 10:33:42 +05:30
exynos_ohci_phy_disable ( & pdev - > dev ) ;
2011-12-23 11:20:54 +09:00
2012-10-03 08:41:37 +09:00
clk_disable_unprepare ( exynos_ohci - > clk ) ;
2011-12-23 11:20:54 +09:00
usb_put_hcd ( hcd ) ;
return 0 ;
}
static void exynos_ohci_shutdown ( struct platform_device * pdev )
{
2013-09-21 16:38:38 +05:30
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
2011-12-23 11:20:54 +09:00
if ( hcd - > driver - > shutdown )
hcd - > driver - > shutdown ( hcd ) ;
}
# ifdef CONFIG_PM
static int exynos_ohci_suspend ( struct device * dev )
{
2013-09-21 16:38:38 +05:30
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
struct exynos_ohci_hcd * exynos_ohci = to_exynos_ohci ( hcd ) ;
2013-11-13 17:40:20 +05:30
bool do_wakeup = device_may_wakeup ( dev ) ;
int rc = ohci_suspend ( hcd , do_wakeup ) ;
2011-12-23 11:20:54 +09:00
2013-11-13 17:40:20 +05:30
if ( rc )
return rc ;
2013-10-14 10:16:58 -07:00
2014-05-05 10:33:42 +05:30
exynos_ohci_phy_disable ( dev ) ;
2012-06-28 16:49:42 +09:00
2012-10-03 08:41:37 +09:00
clk_disable_unprepare ( exynos_ohci - > clk ) ;
2012-06-28 16:49:42 +09:00
2013-11-13 17:40:20 +05:30
return 0 ;
2011-12-23 11:20:54 +09:00
}
static int exynos_ohci_resume ( struct device * dev )
{
2013-09-21 16:38:38 +05:30
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
struct exynos_ohci_hcd * exynos_ohci = to_exynos_ohci ( hcd ) ;
2014-05-05 10:32:57 +05:30
int ret ;
2011-12-23 11:20:54 +09:00
2012-10-03 08:41:37 +09:00
clk_prepare_enable ( exynos_ohci - > clk ) ;
2012-06-28 16:49:42 +09:00
2014-05-05 10:32:57 +05:30
ret = exynos_ohci_phy_enable ( dev ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable USB phy \n " ) ;
clk_disable_unprepare ( exynos_ohci - > clk ) ;
return ret ;
}
2011-12-23 11:20:54 +09:00
2012-10-08 15:11:29 +02:00
ohci_resume ( hcd , false ) ;
2011-12-23 11:20:54 +09:00
return 0 ;
}
# else
# define exynos_ohci_suspend NULL
# define exynos_ohci_resume NULL
# endif
2013-09-21 16:38:38 +05:30
static const struct ohci_driver_overrides exynos_overrides __initconst = {
. extra_priv_size = sizeof ( struct exynos_ohci_hcd ) ,
} ;
2011-12-23 11:20:54 +09:00
static const struct dev_pm_ops exynos_ohci_pm_ops = {
. suspend = exynos_ohci_suspend ,
. resume = exynos_ohci_resume ,
} ;
2012-07-16 11:25:36 +05:30
# ifdef CONFIG_OF
static const struct of_device_id exynos_ohci_match [ ] = {
2013-01-24 19:15:29 +05:30
{ . compatible = " samsung,exynos4210-ohci " } ,
2012-07-16 11:25:36 +05:30
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , exynos_ohci_match ) ;
# endif
2011-12-23 11:20:54 +09:00
static struct platform_driver exynos_ohci_driver = {
. probe = exynos_ohci_probe ,
2012-11-19 13:21:08 -05:00
. remove = exynos_ohci_remove ,
2011-12-23 11:20:54 +09:00
. shutdown = exynos_ohci_shutdown ,
. driver = {
. name = " exynos-ohci " ,
. pm = & exynos_ohci_pm_ops ,
2012-07-16 11:25:36 +05:30
. of_match_table = of_match_ptr ( exynos_ohci_match ) ,
2011-12-23 11:20:54 +09:00
}
} ;
2013-09-21 16:38:38 +05:30
static int __init ohci_exynos_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( " %s: " DRIVER_DESC " \n " , hcd_name ) ;
ohci_init_driver ( & exynos_ohci_hc_driver , & exynos_overrides ) ;
return platform_driver_register ( & exynos_ohci_driver ) ;
}
module_init ( ohci_exynos_init ) ;
static void __exit ohci_exynos_cleanup ( void )
{
platform_driver_unregister ( & exynos_ohci_driver ) ;
}
module_exit ( ohci_exynos_cleanup ) ;
2011-12-23 11:20:54 +09:00
MODULE_ALIAS ( " platform:exynos-ohci " ) ;
MODULE_AUTHOR ( " Jingoo Han <jg1.han@samsung.com> " ) ;
2013-09-21 16:38:38 +05:30
MODULE_LICENSE ( " GPL v2 " ) ;