2011-04-08 13:22:10 +09:00
/*
* Copyright ( C ) 2011 Samsung Electronics Co . Ltd
* Author : Joonyoung Shim < jy0922 . shim @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/platform_device.h>
# include <mach/regs-pmu.h>
# include <mach/regs-usb-phy.h>
# include <plat/cpu.h>
# include <plat/usb-phy.h>
2011-12-23 11:19:36 +09:00
static atomic_t host_usage ;
static int exynos4_usb_host_phy_is_on ( void )
{
return ( readl ( EXYNOS4_PHYPWR ) & PHY1_STD_ANALOG_POWERDOWN ) ? 0 : 1 ;
}
2011-04-08 13:22:10 +09:00
static int exynos4_usb_phy1_init ( struct platform_device * pdev )
{
struct clk * otg_clk ;
struct clk * xusbxti_clk ;
u32 phyclk ;
u32 rstcon ;
int err ;
2011-12-23 11:19:36 +09:00
atomic_inc ( & host_usage ) ;
2011-04-08 13:22:10 +09:00
otg_clk = clk_get ( & pdev - > dev , " otg " ) ;
if ( IS_ERR ( otg_clk ) ) {
dev_err ( & pdev - > dev , " Failed to get otg clock \n " ) ;
return PTR_ERR ( otg_clk ) ;
}
err = clk_enable ( otg_clk ) ;
if ( err ) {
clk_put ( otg_clk ) ;
return err ;
}
2011-12-23 11:19:36 +09:00
if ( exynos4_usb_host_phy_is_on ( ) )
return 0 ;
2011-04-08 13:22:10 +09:00
writel ( readl ( S5P_USBHOST_PHY_CONTROL ) | S5P_USBHOST_PHY_ENABLE ,
S5P_USBHOST_PHY_CONTROL ) ;
/* set clock frequency for PLL */
phyclk = readl ( EXYNOS4_PHYCLK ) & ~ CLKSEL_MASK ;
xusbxti_clk = clk_get ( & pdev - > dev , " xusbxti " ) ;
if ( xusbxti_clk & & ! IS_ERR ( xusbxti_clk ) ) {
switch ( clk_get_rate ( xusbxti_clk ) ) {
case 12 * MHZ :
phyclk | = CLKSEL_12M ;
break ;
case 24 * MHZ :
phyclk | = CLKSEL_24M ;
break ;
default :
case 48 * MHZ :
/* default reference clock */
break ;
}
clk_put ( xusbxti_clk ) ;
}
writel ( phyclk , EXYNOS4_PHYCLK ) ;
/* floating prevention logic: disable */
writel ( ( readl ( EXYNOS4_PHY1CON ) | FPENABLEN ) , EXYNOS4_PHY1CON ) ;
/* set to normal HSIC 0 and 1 of PHY1 */
writel ( ( readl ( EXYNOS4_PHYPWR ) & ~ PHY1_HSIC_NORMAL_MASK ) ,
EXYNOS4_PHYPWR ) ;
/* set to normal standard USB of PHY1 */
writel ( ( readl ( EXYNOS4_PHYPWR ) & ~ PHY1_STD_NORMAL_MASK ) , EXYNOS4_PHYPWR ) ;
/* reset all ports of both PHY and Link */
rstcon = readl ( EXYNOS4_RSTCON ) | HOST_LINK_PORT_SWRST_MASK |
PHY1_SWRST_MASK ;
writel ( rstcon , EXYNOS4_RSTCON ) ;
udelay ( 10 ) ;
rstcon & = ~ ( HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK ) ;
writel ( rstcon , EXYNOS4_RSTCON ) ;
2011-08-18 20:40:24 +09:00
udelay ( 80 ) ;
2011-04-08 13:22:10 +09:00
clk_disable ( otg_clk ) ;
clk_put ( otg_clk ) ;
return 0 ;
}
static int exynos4_usb_phy1_exit ( struct platform_device * pdev )
{
struct clk * otg_clk ;
int err ;
2011-12-23 11:19:36 +09:00
if ( atomic_dec_return ( & host_usage ) > 0 )
return 0 ;
2011-04-08 13:22:10 +09:00
otg_clk = clk_get ( & pdev - > dev , " otg " ) ;
if ( IS_ERR ( otg_clk ) ) {
dev_err ( & pdev - > dev , " Failed to get otg clock \n " ) ;
return PTR_ERR ( otg_clk ) ;
}
err = clk_enable ( otg_clk ) ;
if ( err ) {
clk_put ( otg_clk ) ;
return err ;
}
writel ( ( readl ( EXYNOS4_PHYPWR ) | PHY1_STD_ANALOG_POWERDOWN ) ,
EXYNOS4_PHYPWR ) ;
writel ( readl ( S5P_USBHOST_PHY_CONTROL ) & ~ S5P_USBHOST_PHY_ENABLE ,
S5P_USBHOST_PHY_CONTROL ) ;
clk_disable ( otg_clk ) ;
clk_put ( otg_clk ) ;
return 0 ;
}
int s5p_usb_phy_init ( struct platform_device * pdev , int type )
{
if ( type = = S5P_USB_PHY_HOST )
return exynos4_usb_phy1_init ( pdev ) ;
return - EINVAL ;
}
int s5p_usb_phy_exit ( struct platform_device * pdev , int type )
{
if ( type = = S5P_USB_PHY_HOST )
return exynos4_usb_phy1_exit ( pdev ) ;
return - EINVAL ;
}