2021-07-19 15:19:32 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ / G2L USBPHY control driver
*
* Copyright ( C ) 2021 Renesas Electronics Corporation
*/
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/reset.h>
# include <linux/reset-controller.h>
# define RESET 0x000
# define RESET_SEL_PLLRESET BIT(12)
# define RESET_PLLRESET BIT(8)
# define RESET_SEL_P2RESET BIT(5)
# define RESET_SEL_P1RESET BIT(4)
# define RESET_PHYRST_2 BIT(1)
# define RESET_PHYRST_1 BIT(0)
# define PHY_RESET_PORT2 (RESET_SEL_P2RESET | RESET_PHYRST_2)
# define PHY_RESET_PORT1 (RESET_SEL_P1RESET | RESET_PHYRST_1)
# define NUM_PORTS 2
struct rzg2l_usbphy_ctrl_priv {
struct reset_controller_dev rcdev ;
struct reset_control * rstc ;
void __iomem * base ;
spinlock_t lock ;
} ;
# define rcdev_to_priv(x) container_of(x, struct rzg2l_usbphy_ctrl_priv, rcdev)
static int rzg2l_usbphy_ctrl_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct rzg2l_usbphy_ctrl_priv * priv = rcdev_to_priv ( rcdev ) ;
u32 port_mask = PHY_RESET_PORT1 | PHY_RESET_PORT2 ;
void __iomem * base = priv - > base ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
val = readl ( base + RESET ) ;
val | = id ? PHY_RESET_PORT2 : PHY_RESET_PORT1 ;
if ( port_mask = = ( val & port_mask ) )
val | = RESET_PLLRESET ;
writel ( val , base + RESET ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return 0 ;
}
static int rzg2l_usbphy_ctrl_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct rzg2l_usbphy_ctrl_priv * priv = rcdev_to_priv ( rcdev ) ;
void __iomem * base = priv - > base ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
val = readl ( base + RESET ) ;
val | = RESET_SEL_PLLRESET ;
val & = ~ ( RESET_PLLRESET | ( id ? PHY_RESET_PORT2 : PHY_RESET_PORT1 ) ) ;
writel ( val , base + RESET ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return 0 ;
}
static int rzg2l_usbphy_ctrl_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct rzg2l_usbphy_ctrl_priv * priv = rcdev_to_priv ( rcdev ) ;
u32 port_mask ;
port_mask = id ? PHY_RESET_PORT2 : PHY_RESET_PORT1 ;
return ! ! ( readl ( priv - > base + RESET ) & port_mask ) ;
}
static const struct of_device_id rzg2l_usbphy_ctrl_match_table [ ] = {
{ . compatible = " renesas,rzg2l-usbphy-ctrl " } ,
{ /* Sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , rzg2l_usbphy_ctrl_match_table ) ;
static const struct reset_control_ops rzg2l_usbphy_ctrl_reset_ops = {
. assert = rzg2l_usbphy_ctrl_assert ,
. deassert = rzg2l_usbphy_ctrl_deassert ,
. status = rzg2l_usbphy_ctrl_status ,
} ;
static int rzg2l_usbphy_ctrl_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct rzg2l_usbphy_ctrl_priv * priv ;
unsigned long flags ;
int error ;
u32 val ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
priv - > rstc = devm_reset_control_get_exclusive ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( priv - > rstc ) )
return dev_err_probe ( dev , PTR_ERR ( priv - > rstc ) ,
" failed to get reset \n " ) ;
reset_control_deassert ( priv - > rstc ) ;
priv - > rcdev . ops = & rzg2l_usbphy_ctrl_reset_ops ;
priv - > rcdev . of_reset_n_cells = 1 ;
priv - > rcdev . nr_resets = NUM_PORTS ;
priv - > rcdev . of_node = dev - > of_node ;
priv - > rcdev . dev = dev ;
error = devm_reset_controller_register ( dev , & priv - > rcdev ) ;
if ( error )
return error ;
spin_lock_init ( & priv - > lock ) ;
dev_set_drvdata ( dev , priv ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2021-12-15 13:14:23 +03:00
error = pm_runtime_resume_and_get ( & pdev - > dev ) ;
if ( error < 0 ) {
pm_runtime_disable ( & pdev - > dev ) ;
reset_control_assert ( priv - > rstc ) ;
return dev_err_probe ( & pdev - > dev , error , " pm_runtime_resume_and_get failed " ) ;
}
2021-07-19 15:19:32 +03:00
/* put pll and phy into reset state */
spin_lock_irqsave ( & priv - > lock , flags ) ;
val = readl ( priv - > base + RESET ) ;
val | = RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1 ;
writel ( val , priv - > base + RESET ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return 0 ;
}
static int rzg2l_usbphy_ctrl_remove ( struct platform_device * pdev )
{
struct rzg2l_usbphy_ctrl_priv * priv = dev_get_drvdata ( & pdev - > dev ) ;
pm_runtime_put ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
reset_control_assert ( priv - > rstc ) ;
return 0 ;
}
static struct platform_driver rzg2l_usbphy_ctrl_driver = {
. driver = {
. name = " rzg2l_usbphy_ctrl " ,
. of_match_table = rzg2l_usbphy_ctrl_match_table ,
} ,
. probe = rzg2l_usbphy_ctrl_probe ,
. remove = rzg2l_usbphy_ctrl_remove ,
} ;
module_platform_driver ( rzg2l_usbphy_ctrl_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Renesas RZ/G2L USBPHY Control " ) ;
MODULE_AUTHOR ( " biju.das.jz@bp.renesas.com> " ) ;