2019-05-29 07:17:56 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-09-22 15:34:01 -04:00
/*
* phy - brcm - usb . c - Broadcom USB Phy Driver
*
* Copyright ( C ) 2015 - 2017 Broadcom
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
2020-12-16 15:33:04 +01:00
# include <linux/of_device.h>
2017-09-22 15:34:01 -04:00
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/soc/brcmstb/brcmstb.h>
# include <dt-bindings/phy/phy.h>
2020-01-03 13:18:06 -05:00
# include <linux/mfd/syscon.h>
2017-09-22 15:34:01 -04:00
# include "phy-brcm-usb-init.h"
2017-09-22 15:34:02 -04:00
static DEFINE_MUTEX ( sysfs_lock ) ;
2017-09-22 15:34:01 -04:00
enum brcm_usb_phy_id {
BRCM_USB_PHY_2_0 = 0 ,
BRCM_USB_PHY_3_0 ,
BRCM_USB_PHY_ID_MAX
} ;
struct value_to_name_map {
int value ;
const char * name ;
} ;
2020-01-03 13:18:06 -05:00
struct match_chip_info {
2020-12-16 15:33:05 +01:00
void ( * init_func ) ( struct brcm_usb_init_params * params ) ;
2020-01-03 13:18:06 -05:00
u8 required_regs [ BRCM_REGS_MAX + 1 ] ;
2020-01-03 13:18:09 -05:00
u8 optional_reg ;
2020-01-03 13:18:06 -05:00
} ;
2020-05-16 14:04:41 +02:00
static const struct value_to_name_map brcm_dr_mode_to_name [ ] = {
2017-09-22 15:34:01 -04:00
{ USB_CTLR_MODE_HOST , " host " } ,
{ USB_CTLR_MODE_DEVICE , " peripheral " } ,
{ USB_CTLR_MODE_DRD , " drd " } ,
{ USB_CTLR_MODE_TYPEC_PD , " typec-pd " }
} ;
2020-05-16 14:04:41 +02:00
static const struct value_to_name_map brcm_dual_mode_to_name [ ] = {
2017-09-22 15:34:02 -04:00
{ 0 , " host " } ,
{ 1 , " device " } ,
{ 2 , " auto " } ,
} ;
2017-09-22 15:34:01 -04:00
struct brcm_usb_phy {
struct phy * phy ;
unsigned int id ;
bool inited ;
} ;
struct brcm_usb_phy_data {
struct brcm_usb_init_params ini ;
bool has_eohci ;
bool has_xhci ;
struct clk * usb_20_clk ;
struct clk * usb_30_clk ;
2020-01-03 13:18:02 -05:00
struct clk * suspend_clk ;
2017-09-22 15:34:01 -04:00
struct mutex mutex ; /* serialize phy init */
int init_count ;
2020-01-03 13:18:02 -05:00
int wake_irq ;
2017-09-22 15:34:01 -04:00
struct brcm_usb_phy phys [ BRCM_USB_PHY_ID_MAX ] ;
} ;
2020-01-03 13:18:06 -05:00
static s8 * node_reg_names [ BRCM_REGS_MAX ] = {
2020-01-03 13:18:09 -05:00
" crtl " , " xhci_ec " , " xhci_gbl " , " usb_phy " , " usb_mdio " , " bdc_ec "
2020-01-03 13:18:06 -05:00
} ;
2020-01-03 13:18:02 -05:00
static irqreturn_t brcm_usb_phy_wake_isr ( int irq , void * dev_id )
{
struct phy * gphy = dev_id ;
pm_wakeup_event ( & gphy - > dev , 0 ) ;
return IRQ_HANDLED ;
}
2017-09-22 15:34:01 -04:00
static int brcm_usb_phy_init ( struct phy * gphy )
{
struct brcm_usb_phy * phy = phy_get_drvdata ( gphy ) ;
struct brcm_usb_phy_data * priv =
container_of ( phy , struct brcm_usb_phy_data , phys [ phy - > id ] ) ;
/*
* Use a lock to make sure a second caller waits until
* the base phy is inited before using it .
*/
mutex_lock ( & priv - > mutex ) ;
if ( priv - > init_count + + = = 0 ) {
2020-01-03 13:18:00 -05:00
clk_prepare_enable ( priv - > usb_20_clk ) ;
clk_prepare_enable ( priv - > usb_30_clk ) ;
2020-01-03 13:18:02 -05:00
clk_prepare_enable ( priv - > suspend_clk ) ;
2017-09-22 15:34:01 -04:00
brcm_usb_init_common ( & priv - > ini ) ;
}
mutex_unlock ( & priv - > mutex ) ;
if ( phy - > id = = BRCM_USB_PHY_2_0 )
brcm_usb_init_eohci ( & priv - > ini ) ;
else if ( phy - > id = = BRCM_USB_PHY_3_0 )
brcm_usb_init_xhci ( & priv - > ini ) ;
phy - > inited = true ;
dev_dbg ( & gphy - > dev , " INIT, id: %d, total: %d \n " , phy - > id ,
priv - > init_count ) ;
return 0 ;
}
static int brcm_usb_phy_exit ( struct phy * gphy )
{
struct brcm_usb_phy * phy = phy_get_drvdata ( gphy ) ;
struct brcm_usb_phy_data * priv =
container_of ( phy , struct brcm_usb_phy_data , phys [ phy - > id ] ) ;
dev_dbg ( & gphy - > dev , " EXIT \n " ) ;
if ( phy - > id = = BRCM_USB_PHY_2_0 )
brcm_usb_uninit_eohci ( & priv - > ini ) ;
if ( phy - > id = = BRCM_USB_PHY_3_0 )
brcm_usb_uninit_xhci ( & priv - > ini ) ;
/* If both xhci and eohci are gone, reset everything else */
mutex_lock ( & priv - > mutex ) ;
if ( - - priv - > init_count = = 0 ) {
brcm_usb_uninit_common ( & priv - > ini ) ;
2020-01-03 13:18:00 -05:00
clk_disable_unprepare ( priv - > usb_20_clk ) ;
clk_disable_unprepare ( priv - > usb_30_clk ) ;
2020-01-03 13:18:02 -05:00
clk_disable_unprepare ( priv - > suspend_clk ) ;
2017-09-22 15:34:01 -04:00
}
mutex_unlock ( & priv - > mutex ) ;
phy - > inited = false ;
return 0 ;
}
2020-05-16 14:04:41 +02:00
static const struct phy_ops brcm_usb_phy_ops = {
2017-09-22 15:34:01 -04:00
. init = brcm_usb_phy_init ,
. exit = brcm_usb_phy_exit ,
. owner = THIS_MODULE ,
} ;
static struct phy * brcm_usb_phy_xlate ( struct device * dev ,
struct of_phandle_args * args )
{
struct brcm_usb_phy_data * data = dev_get_drvdata ( dev ) ;
/*
* values 0 and 1 are for backward compatibility with
* device tree nodes from older bootloaders .
*/
switch ( args - > args [ 0 ] ) {
case 0 :
case PHY_TYPE_USB2 :
if ( data - > phys [ BRCM_USB_PHY_2_0 ] . phy )
return data - > phys [ BRCM_USB_PHY_2_0 ] . phy ;
dev_warn ( dev , " Error, 2.0 Phy not found \n " ) ;
break ;
case 1 :
case PHY_TYPE_USB3 :
if ( data - > phys [ BRCM_USB_PHY_3_0 ] . phy )
return data - > phys [ BRCM_USB_PHY_3_0 ] . phy ;
dev_warn ( dev , " Error, 3.0 Phy not found \n " ) ;
break ;
}
return ERR_PTR ( - ENODEV ) ;
}
2020-05-16 14:04:41 +02:00
static int name_to_value ( const struct value_to_name_map * table , int count ,
2017-09-22 15:34:01 -04:00
const char * name , int * value )
{
int x ;
* value = 0 ;
for ( x = 0 ; x < count ; x + + ) {
if ( sysfs_streq ( name , table [ x ] . name ) ) {
* value = x ;
return 0 ;
}
}
return - EINVAL ;
}
2020-05-16 14:04:41 +02:00
static const char * value_to_name ( const struct value_to_name_map * table , int count ,
2017-09-22 15:34:02 -04:00
int value )
{
if ( value > = count )
return " unknown " ;
return table [ value ] . name ;
}
static ssize_t dr_mode_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct brcm_usb_phy_data * priv = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %s \n " ,
value_to_name ( & brcm_dr_mode_to_name [ 0 ] ,
ARRAY_SIZE ( brcm_dr_mode_to_name ) ,
priv - > ini . mode ) ) ;
}
static DEVICE_ATTR_RO ( dr_mode ) ;
static ssize_t dual_select_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
struct brcm_usb_phy_data * priv = dev_get_drvdata ( dev ) ;
int value ;
int res ;
mutex_lock ( & sysfs_lock ) ;
res = name_to_value ( & brcm_dual_mode_to_name [ 0 ] ,
ARRAY_SIZE ( brcm_dual_mode_to_name ) , buf , & value ) ;
if ( ! res ) {
2020-01-03 13:18:03 -05:00
brcm_usb_set_dual_select ( & priv - > ini , value ) ;
2017-09-22 15:34:02 -04:00
res = len ;
}
mutex_unlock ( & sysfs_lock ) ;
return res ;
}
static ssize_t dual_select_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct brcm_usb_phy_data * priv = dev_get_drvdata ( dev ) ;
int value ;
mutex_lock ( & sysfs_lock ) ;
2020-01-03 13:18:03 -05:00
value = brcm_usb_get_dual_select ( & priv - > ini ) ;
2017-09-22 15:34:02 -04:00
mutex_unlock ( & sysfs_lock ) ;
return sprintf ( buf , " %s \n " ,
value_to_name ( & brcm_dual_mode_to_name [ 0 ] ,
ARRAY_SIZE ( brcm_dual_mode_to_name ) ,
value ) ) ;
}
static DEVICE_ATTR_RW ( dual_select ) ;
static struct attribute * brcm_usb_phy_attrs [ ] = {
& dev_attr_dr_mode . attr ,
& dev_attr_dual_select . attr ,
NULL
} ;
static const struct attribute_group brcm_usb_phy_group = {
. attrs = brcm_usb_phy_attrs ,
} ;
2020-05-16 14:04:41 +02:00
static const struct match_chip_info chip_info_7216 = {
2020-01-03 13:18:06 -05:00
. init_func = & brcm_usb_dvr_init_7216 ,
. required_regs = {
BRCM_REGS_CTRL ,
BRCM_REGS_XHCI_EC ,
BRCM_REGS_XHCI_GBL ,
- 1 ,
} ,
} ;
2020-05-16 14:04:41 +02:00
static const struct match_chip_info chip_info_7211b0 = {
2020-01-03 13:18:06 -05:00
. init_func = & brcm_usb_dvr_init_7211b0 ,
. required_regs = {
BRCM_REGS_CTRL ,
BRCM_REGS_XHCI_EC ,
BRCM_REGS_XHCI_GBL ,
BRCM_REGS_USB_PHY ,
BRCM_REGS_USB_MDIO ,
- 1 ,
} ,
2020-01-03 13:18:09 -05:00
. optional_reg = BRCM_REGS_BDC_EC ,
2020-01-03 13:18:06 -05:00
} ;
2020-05-16 14:04:41 +02:00
static const struct match_chip_info chip_info_7445 = {
2020-01-03 13:18:06 -05:00
. init_func = & brcm_usb_dvr_init_7445 ,
. required_regs = {
BRCM_REGS_CTRL ,
BRCM_REGS_XHCI_EC ,
- 1 ,
} ,
} ;
2020-01-03 13:18:05 -05:00
static const struct of_device_id brcm_usb_dt_ids [ ] = {
2021-01-06 21:58:38 +01:00
{
. compatible = " brcm,bcm4908-usb-phy " ,
. data = & chip_info_7445 ,
} ,
2020-01-03 13:18:05 -05:00
{
. compatible = " brcm,bcm7216-usb-phy " ,
2020-01-03 13:18:06 -05:00
. data = & chip_info_7216 ,
} ,
{
. compatible = " brcm,bcm7211-usb-phy " ,
. data = & chip_info_7211b0 ,
} ,
{
. compatible = " brcm,brcmstb-usb-phy " ,
. data = & chip_info_7445 ,
2020-01-03 13:18:05 -05:00
} ,
{ /* sentinel */ }
} ;
2020-01-03 13:18:06 -05:00
static int brcm_usb_get_regs ( struct platform_device * pdev ,
enum brcmusb_reg_sel regs ,
2020-01-03 13:18:09 -05:00
struct brcm_usb_init_params * ini ,
bool optional )
2020-01-03 13:18:06 -05:00
{
struct resource * res ;
/* Older DT nodes have ctrl and optional xhci_ec by index only */
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
node_reg_names [ regs ] ) ;
if ( res = = NULL ) {
if ( regs = = BRCM_REGS_CTRL ) {
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
} else if ( regs = = BRCM_REGS_XHCI_EC ) {
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
/* XHCI_EC registers are optional */
if ( res = = NULL )
return 0 ;
}
if ( res = = NULL ) {
2020-01-03 13:18:09 -05:00
if ( optional ) {
dev_dbg ( & pdev - > dev ,
" Optional reg %s not found \n " ,
node_reg_names [ regs ] ) ;
return 0 ;
}
dev_err ( & pdev - > dev , " can't get %s base addr \n " ,
2020-01-03 13:18:06 -05:00
node_reg_names [ regs ] ) ;
return 1 ;
}
}
ini - > regs [ regs ] = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( ini - > regs [ regs ] ) ) {
dev_err ( & pdev - > dev , " can't map %s register space \n " ,
node_reg_names [ regs ] ) ;
return 1 ;
}
return 0 ;
}
2020-01-03 13:18:02 -05:00
static int brcm_usb_phy_dvr_init ( struct platform_device * pdev ,
2017-09-22 15:34:01 -04:00
struct brcm_usb_phy_data * priv ,
struct device_node * dn )
{
2020-01-03 13:18:02 -05:00
struct device * dev = & pdev - > dev ;
struct phy * gphy = NULL ;
2017-09-22 15:34:01 -04:00
int err ;
priv - > usb_20_clk = of_clk_get_by_name ( dn , " sw_usb " ) ;
if ( IS_ERR ( priv - > usb_20_clk ) ) {
2020-01-03 13:18:07 -05:00
if ( PTR_ERR ( priv - > usb_20_clk ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
2017-09-22 15:34:01 -04:00
dev_info ( dev , " Clock not found in Device Tree \n " ) ;
priv - > usb_20_clk = NULL ;
}
err = clk_prepare_enable ( priv - > usb_20_clk ) ;
if ( err )
return err ;
if ( priv - > has_eohci ) {
gphy = devm_phy_create ( dev , NULL , & brcm_usb_phy_ops ) ;
if ( IS_ERR ( gphy ) ) {
dev_err ( dev , " failed to create EHCI/OHCI PHY \n " ) ;
return PTR_ERR ( gphy ) ;
}
priv - > phys [ BRCM_USB_PHY_2_0 ] . phy = gphy ;
priv - > phys [ BRCM_USB_PHY_2_0 ] . id = BRCM_USB_PHY_2_0 ;
phy_set_drvdata ( gphy , & priv - > phys [ BRCM_USB_PHY_2_0 ] ) ;
}
if ( priv - > has_xhci ) {
gphy = devm_phy_create ( dev , NULL , & brcm_usb_phy_ops ) ;
if ( IS_ERR ( gphy ) ) {
dev_err ( dev , " failed to create XHCI PHY \n " ) ;
return PTR_ERR ( gphy ) ;
}
priv - > phys [ BRCM_USB_PHY_3_0 ] . phy = gphy ;
priv - > phys [ BRCM_USB_PHY_3_0 ] . id = BRCM_USB_PHY_3_0 ;
phy_set_drvdata ( gphy , & priv - > phys [ BRCM_USB_PHY_3_0 ] ) ;
priv - > usb_30_clk = of_clk_get_by_name ( dn , " sw_usb3 " ) ;
if ( IS_ERR ( priv - > usb_30_clk ) ) {
2020-01-03 13:18:07 -05:00
if ( PTR_ERR ( priv - > usb_30_clk ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
2017-09-22 15:34:01 -04:00
dev_info ( dev ,
" USB3.0 clock not found in Device Tree \n " ) ;
priv - > usb_30_clk = NULL ;
}
err = clk_prepare_enable ( priv - > usb_30_clk ) ;
if ( err )
return err ;
}
2020-01-03 13:18:02 -05:00
priv - > suspend_clk = clk_get ( dev , " usb0_freerun " ) ;
if ( IS_ERR ( priv - > suspend_clk ) ) {
2020-01-03 13:18:07 -05:00
if ( PTR_ERR ( priv - > suspend_clk ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
2020-01-03 13:18:02 -05:00
dev_err ( dev , " Suspend Clock not found in Device Tree \n " ) ;
priv - > suspend_clk = NULL ;
}
priv - > wake_irq = platform_get_irq_byname ( pdev , " wake " ) ;
if ( priv - > wake_irq < 0 )
priv - > wake_irq = platform_get_irq_byname ( pdev , " wakeup " ) ;
if ( priv - > wake_irq > = 0 ) {
err = devm_request_irq ( dev , priv - > wake_irq ,
brcm_usb_phy_wake_isr , 0 ,
dev_name ( dev ) , gphy ) ;
if ( err < 0 )
return err ;
device_set_wakeup_capable ( dev , 1 ) ;
} else {
dev_info ( dev ,
" Wake interrupt missing, system wake not supported \n " ) ;
}
2017-09-22 15:34:01 -04:00
return 0 ;
}
static int brcm_usb_phy_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct brcm_usb_phy_data * priv ;
struct phy_provider * phy_provider ;
struct device_node * dn = pdev - > dev . of_node ;
int err ;
const char * mode ;
2020-01-03 13:18:06 -05:00
const struct match_chip_info * info ;
struct regmap * rmap ;
int x ;
2017-09-22 15:34:01 -04:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
platform_set_drvdata ( pdev , priv ) ;
priv - > ini . family_id = brcmstb_get_family_id ( ) ;
priv - > ini . product_id = brcmstb_get_product_id ( ) ;
2020-01-03 13:18:05 -05:00
2020-12-16 15:33:04 +01:00
info = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! info )
return - ENOENT ;
2020-12-16 15:33:05 +01:00
info - > init_func ( & priv - > ini ) ;
2020-01-03 13:18:05 -05:00
2017-09-22 15:34:01 -04:00
dev_dbg ( dev , " Best mapping table is for %s \n " ,
priv - > ini . family_name ) ;
2020-01-03 13:18:05 -05:00
2017-09-22 15:34:01 -04:00
of_property_read_u32 ( dn , " brcm,ipp " , & priv - > ini . ipp ) ;
of_property_read_u32 ( dn , " brcm,ioc " , & priv - > ini . ioc ) ;
priv - > ini . mode = USB_CTLR_MODE_HOST ;
err = of_property_read_string ( dn , " dr_mode " , & mode ) ;
if ( err = = 0 ) {
name_to_value ( & brcm_dr_mode_to_name [ 0 ] ,
ARRAY_SIZE ( brcm_dr_mode_to_name ) ,
mode , & priv - > ini . mode ) ;
}
2017-12-27 14:28:48 -05:00
if ( of_property_read_bool ( dn , " brcm,has-xhci " ) )
2017-09-22 15:34:01 -04:00
priv - > has_xhci = true ;
2017-12-27 14:28:48 -05:00
if ( of_property_read_bool ( dn , " brcm,has-eohci " ) )
2017-09-22 15:34:01 -04:00
priv - > has_eohci = true ;
2020-01-03 13:18:06 -05:00
for ( x = 0 ; x < BRCM_REGS_MAX ; x + + ) {
if ( info - > required_regs [ x ] > = BRCM_REGS_MAX )
break ;
err = brcm_usb_get_regs ( pdev , info - > required_regs [ x ] ,
2020-01-03 13:18:09 -05:00
& priv - > ini , false ) ;
if ( err )
return - EINVAL ;
}
if ( info - > optional_reg ) {
err = brcm_usb_get_regs ( pdev , info - > optional_reg ,
& priv - > ini , true ) ;
2020-01-03 13:18:06 -05:00
if ( err )
return - EINVAL ;
}
2020-01-03 13:18:02 -05:00
err = brcm_usb_phy_dvr_init ( pdev , priv , dn ) ;
2017-09-22 15:34:01 -04:00
if ( err )
return err ;
mutex_init ( & priv - > mutex ) ;
/* make sure invert settings are correct */
brcm_usb_init_ipp ( & priv - > ini ) ;
2017-09-22 15:34:02 -04:00
/*
* Create sysfs entries for mode .
* Remove " dual_select " attribute if not in dual mode
*/
if ( priv - > ini . mode ! = USB_CTLR_MODE_DRD )
brcm_usb_phy_attrs [ 1 ] = NULL ;
err = sysfs_create_group ( & dev - > kobj , & brcm_usb_phy_group ) ;
if ( err )
dev_warn ( dev , " Error creating sysfs attributes \n " ) ;
2020-01-03 13:18:06 -05:00
/* Get piarbctl syscon if it exists */
rmap = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" syscon-piarbctl " ) ;
if ( IS_ERR ( rmap ) )
rmap = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" brcm,syscon-piarbctl " ) ;
if ( ! IS_ERR ( rmap ) )
priv - > ini . syscon_piarbctl = rmap ;
2017-09-22 15:34:01 -04:00
/* start with everything off */
if ( priv - > has_xhci )
brcm_usb_uninit_xhci ( & priv - > ini ) ;
if ( priv - > has_eohci )
brcm_usb_uninit_eohci ( & priv - > ini ) ;
brcm_usb_uninit_common ( & priv - > ini ) ;
2020-01-03 13:18:00 -05:00
clk_disable_unprepare ( priv - > usb_20_clk ) ;
clk_disable_unprepare ( priv - > usb_30_clk ) ;
2017-09-22 15:34:01 -04:00
phy_provider = devm_of_phy_provider_register ( dev , brcm_usb_phy_xlate ) ;
2018-08-16 23:58:54 +08:00
return PTR_ERR_OR_ZERO ( phy_provider ) ;
2017-09-22 15:34:01 -04:00
}
2019-05-22 11:35:25 -07:00
static int brcm_usb_phy_remove ( struct platform_device * pdev )
{
sysfs_remove_group ( & pdev - > dev . kobj , & brcm_usb_phy_group ) ;
return 0 ;
}
2017-09-22 15:34:01 -04:00
# ifdef CONFIG_PM_SLEEP
static int brcm_usb_phy_suspend ( struct device * dev )
{
struct brcm_usb_phy_data * priv = dev_get_drvdata ( dev ) ;
if ( priv - > init_count ) {
2020-01-03 13:18:11 -05:00
priv - > ini . wake_enabled = device_may_wakeup ( dev ) ;
2020-01-03 13:18:01 -05:00
if ( priv - > phys [ BRCM_USB_PHY_3_0 ] . inited )
brcm_usb_uninit_xhci ( & priv - > ini ) ;
if ( priv - > phys [ BRCM_USB_PHY_2_0 ] . inited )
brcm_usb_uninit_eohci ( & priv - > ini ) ;
brcm_usb_uninit_common ( & priv - > ini ) ;
2020-01-03 13:18:11 -05:00
/*
* Handle the clocks unless needed for wake . This has
* to work for both older XHCI - > 3.0 - clks , EOHCI - > 2.0 - clks
* and newer XHCI - > 2.0 - clks / 3.0 - clks .
*/
if ( ! priv - > ini . suspend_with_clocks ) {
if ( priv - > phys [ BRCM_USB_PHY_3_0 ] . inited )
clk_disable_unprepare ( priv - > usb_30_clk ) ;
if ( priv - > phys [ BRCM_USB_PHY_2_0 ] . inited | |
! priv - > has_eohci )
clk_disable_unprepare ( priv - > usb_20_clk ) ;
}
2020-01-03 13:18:02 -05:00
if ( priv - > wake_irq > = 0 )
enable_irq_wake ( priv - > wake_irq ) ;
2017-09-22 15:34:01 -04:00
}
return 0 ;
}
static int brcm_usb_phy_resume ( struct device * dev )
{
struct brcm_usb_phy_data * priv = dev_get_drvdata ( dev ) ;
2020-01-03 13:18:00 -05:00
clk_prepare_enable ( priv - > usb_20_clk ) ;
clk_prepare_enable ( priv - > usb_30_clk ) ;
2017-09-22 15:34:01 -04:00
brcm_usb_init_ipp ( & priv - > ini ) ;
/*
* Initialize anything that was previously initialized .
* Uninitialize anything that wasn ' t previously initialized .
*/
if ( priv - > init_count ) {
2020-01-03 13:18:02 -05:00
if ( priv - > wake_irq > = 0 )
disable_irq_wake ( priv - > wake_irq ) ;
2017-09-22 15:34:01 -04:00
brcm_usb_init_common ( & priv - > ini ) ;
if ( priv - > phys [ BRCM_USB_PHY_2_0 ] . inited ) {
brcm_usb_init_eohci ( & priv - > ini ) ;
} else if ( priv - > has_eohci ) {
brcm_usb_uninit_eohci ( & priv - > ini ) ;
2020-01-03 13:18:00 -05:00
clk_disable_unprepare ( priv - > usb_20_clk ) ;
2017-09-22 15:34:01 -04:00
}
if ( priv - > phys [ BRCM_USB_PHY_3_0 ] . inited ) {
brcm_usb_init_xhci ( & priv - > ini ) ;
} else if ( priv - > has_xhci ) {
brcm_usb_uninit_xhci ( & priv - > ini ) ;
2020-01-03 13:18:00 -05:00
clk_disable_unprepare ( priv - > usb_30_clk ) ;
2020-01-03 13:18:11 -05:00
if ( ! priv - > has_eohci )
clk_disable_unprepare ( priv - > usb_20_clk ) ;
2017-09-22 15:34:01 -04:00
}
} else {
if ( priv - > has_xhci )
brcm_usb_uninit_xhci ( & priv - > ini ) ;
if ( priv - > has_eohci )
brcm_usb_uninit_eohci ( & priv - > ini ) ;
brcm_usb_uninit_common ( & priv - > ini ) ;
2020-01-03 13:18:00 -05:00
clk_disable_unprepare ( priv - > usb_20_clk ) ;
clk_disable_unprepare ( priv - > usb_30_clk ) ;
2017-09-22 15:34:01 -04:00
}
2020-01-03 13:18:11 -05:00
priv - > ini . wake_enabled = false ;
2017-09-22 15:34:01 -04:00
return 0 ;
}
# endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops brcm_usb_phy_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS ( brcm_usb_phy_suspend , brcm_usb_phy_resume )
} ;
MODULE_DEVICE_TABLE ( of , brcm_usb_dt_ids ) ;
static struct platform_driver brcm_usb_driver = {
. probe = brcm_usb_phy_probe ,
2019-05-22 11:35:25 -07:00
. remove = brcm_usb_phy_remove ,
2017-09-22 15:34:01 -04:00
. driver = {
. name = " brcmstb-usb-phy " ,
. pm = & brcm_usb_phy_pm_ops ,
. of_match_table = brcm_usb_dt_ids ,
} ,
} ;
module_platform_driver ( brcm_usb_driver ) ;
MODULE_ALIAS ( " platform:brcmstb-usb-phy " ) ;
MODULE_AUTHOR ( " Al Cooper <acooper@broadcom.com> " ) ;
MODULE_DESCRIPTION ( " BRCM USB PHY driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;