2013-01-25 14:23:57 +04:00
/*
2014-03-06 18:38:37 +04:00
* omap - control - phy . c - The PHY part of control module .
2013-01-25 14:23:57 +04:00
*
* Copyright ( C ) 2013 Texas Instruments Incorporated - http : //www.ti.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 .
*
* Author : Kishon Vijay Abraham I < kishon @ ti . com >
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/of.h>
2013-10-03 19:12:31 +04:00
# include <linux/of_device.h>
2013-01-25 14:23:57 +04:00
# include <linux/err.h>
# include <linux/io.h>
# include <linux/clk.h>
2014-03-06 18:38:37 +04:00
# include <linux/phy/omap_control_phy.h>
2013-01-25 14:23:57 +04:00
/**
2014-03-06 18:38:37 +04:00
* omap_control_phy_power - power on / off the phy using control module reg
2013-01-25 14:23:57 +04:00
* @ dev : the control module device
2013-10-03 19:12:31 +04:00
* @ on : 0 or 1 , based on powering on or off the PHY
2013-01-25 14:23:57 +04:00
*/
2014-03-06 18:38:37 +04:00
void omap_control_phy_power ( struct device * dev , int on )
2013-01-25 14:23:57 +04:00
{
u32 val ;
unsigned long rate ;
2014-03-06 18:38:37 +04:00
struct omap_control_phy * control_phy ;
2013-01-25 14:23:57 +04:00
2013-10-03 19:12:31 +04:00
if ( IS_ERR ( dev ) | | ! dev ) {
pr_err ( " %s: invalid device \n " , __func__ ) ;
2013-01-25 14:23:57 +04:00
return ;
2013-10-03 19:12:31 +04:00
}
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
control_phy = dev_get_drvdata ( dev ) ;
if ( ! control_phy ) {
dev_err ( dev , " %s: invalid control phy device \n " , __func__ ) ;
2013-10-03 19:12:31 +04:00
return ;
2013-01-25 14:23:57 +04:00
}
2014-03-06 18:38:37 +04:00
if ( control_phy - > type = = OMAP_CTRL_TYPE_OTGHS )
2013-10-03 19:12:31 +04:00
return ;
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
val = readl ( control_phy - > power ) ;
2013-10-03 19:12:31 +04:00
2014-03-06 18:38:37 +04:00
switch ( control_phy - > type ) {
2013-10-03 19:12:31 +04:00
case OMAP_CTRL_TYPE_USB2 :
if ( on )
val & = ~ OMAP_CTRL_DEV_PHY_PD ;
else
val | = OMAP_CTRL_DEV_PHY_PD ;
break ;
2013-01-25 14:23:57 +04:00
2013-10-03 19:12:31 +04:00
case OMAP_CTRL_TYPE_PIPE3 :
2014-03-06 18:38:37 +04:00
rate = clk_get_rate ( control_phy - > sys_clk ) ;
2013-10-03 19:12:31 +04:00
rate = rate / 1000000 ;
if ( on ) {
2014-03-06 18:38:37 +04:00
val & = ~ ( OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK ) ;
val | = OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON < <
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT ;
val | = rate < <
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT ;
2013-10-03 19:12:31 +04:00
} else {
2014-03-06 18:38:37 +04:00
val & = ~ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK ;
val | = OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF < <
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT ;
2013-10-03 19:12:31 +04:00
}
break ;
2013-01-25 14:23:57 +04:00
2013-10-03 19:12:31 +04:00
case OMAP_CTRL_TYPE_DRA7USB2 :
if ( on )
val & = ~ OMAP_CTRL_USB2_PHY_PD ;
else
val | = OMAP_CTRL_USB2_PHY_PD ;
break ;
2013-10-15 14:02:14 +04:00
case OMAP_CTRL_TYPE_AM437USB2 :
if ( on ) {
val & = ~ ( AM437X_CTRL_USB2_PHY_PD |
AM437X_CTRL_USB2_OTG_PD ) ;
val | = ( AM437X_CTRL_USB2_OTGVDET_EN |
AM437X_CTRL_USB2_OTGSESSEND_EN ) ;
} else {
val & = ~ ( AM437X_CTRL_USB2_OTGVDET_EN |
AM437X_CTRL_USB2_OTGSESSEND_EN ) ;
val | = ( AM437X_CTRL_USB2_PHY_PD |
AM437X_CTRL_USB2_OTG_PD ) ;
}
break ;
2013-10-03 19:12:31 +04:00
default :
dev_err ( dev , " %s: type %d not recognized \n " ,
2014-03-06 18:38:37 +04:00
__func__ , control_phy - > type ) ;
2013-10-03 19:12:31 +04:00
break ;
}
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
writel ( val , control_phy - > power ) ;
2013-01-25 14:23:57 +04:00
}
2014-03-06 18:38:37 +04:00
EXPORT_SYMBOL_GPL ( omap_control_phy_power ) ;
2013-01-25 14:23:57 +04:00
/**
* omap_control_usb_host_mode - set AVALID , VBUSVALID and ID pin in grounded
2014-03-06 18:38:37 +04:00
* @ ctrl_phy : struct omap_control_phy *
2013-01-25 14:23:57 +04:00
*
* Writes to the mailbox register to notify the usb core that a usb
* device has been connected .
*/
2014-03-06 18:38:37 +04:00
static void omap_control_usb_host_mode ( struct omap_control_phy * ctrl_phy )
2013-01-25 14:23:57 +04:00
{
u32 val ;
2014-03-06 18:38:37 +04:00
val = readl ( ctrl_phy - > otghs_control ) ;
2013-01-25 14:23:57 +04:00
val & = ~ ( OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND ) ;
val | = OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID ;
2014-03-06 18:38:37 +04:00
writel ( val , ctrl_phy - > otghs_control ) ;
2013-01-25 14:23:57 +04:00
}
/**
* omap_control_usb_device_mode - set AVALID , VBUSVALID and ID pin in high
* impedance
2014-03-06 18:38:37 +04:00
* @ ctrl_phy : struct omap_control_phy *
2013-01-25 14:23:57 +04:00
*
* Writes to the mailbox register to notify the usb core that it has been
* connected to a usb host .
*/
2014-03-06 18:38:37 +04:00
static void omap_control_usb_device_mode ( struct omap_control_phy * ctrl_phy )
2013-01-25 14:23:57 +04:00
{
u32 val ;
2014-03-06 18:38:37 +04:00
val = readl ( ctrl_phy - > otghs_control ) ;
2013-01-25 14:23:57 +04:00
val & = ~ OMAP_CTRL_DEV_SESSEND ;
val | = OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
OMAP_CTRL_DEV_VBUSVALID ;
2014-03-06 18:38:37 +04:00
writel ( val , ctrl_phy - > otghs_control ) ;
2013-01-25 14:23:57 +04:00
}
/**
* omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
* impedance
2014-03-06 18:38:37 +04:00
* @ ctrl_phy : struct omap_control_phy *
2013-01-25 14:23:57 +04:00
*
* Writes to the mailbox register to notify the usb core it ' s now in
* disconnected state .
*/
2014-03-06 18:38:37 +04:00
static void omap_control_usb_set_sessionend ( struct omap_control_phy * ctrl_phy )
2013-01-25 14:23:57 +04:00
{
u32 val ;
2014-03-06 18:38:37 +04:00
val = readl ( ctrl_phy - > otghs_control ) ;
2013-01-25 14:23:57 +04:00
val & = ~ ( OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID ) ;
val | = OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND ;
2014-03-06 18:38:37 +04:00
writel ( val , ctrl_phy - > otghs_control ) ;
2013-01-25 14:23:57 +04:00
}
/**
* omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
* or device mode or to denote disconnected state
* @ dev : the control module device
* @ mode : The mode to which usb should be configured
*
* This is an API to write to the mailbox register to notify the usb core that
* a usb device has been connected .
*/
void omap_control_usb_set_mode ( struct device * dev ,
enum omap_control_usb_mode mode )
{
2014-03-06 18:38:37 +04:00
struct omap_control_phy * ctrl_phy ;
2013-01-25 14:23:57 +04:00
2013-10-03 19:12:35 +04:00
if ( IS_ERR ( dev ) | | ! dev )
2013-01-25 14:23:57 +04:00
return ;
2014-03-06 18:38:37 +04:00
ctrl_phy = dev_get_drvdata ( dev ) ;
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
if ( ! ctrl_phy ) {
dev_err ( dev , " Invalid control phy device \n " ) ;
2013-10-03 19:12:35 +04:00
return ;
}
2014-03-06 18:38:37 +04:00
if ( ctrl_phy - > type ! = OMAP_CTRL_TYPE_OTGHS )
2013-10-03 19:12:35 +04:00
return ;
2013-01-25 14:23:57 +04:00
switch ( mode ) {
case USB_MODE_HOST :
2014-03-06 18:38:37 +04:00
omap_control_usb_host_mode ( ctrl_phy ) ;
2013-01-25 14:23:57 +04:00
break ;
case USB_MODE_DEVICE :
2014-03-06 18:38:37 +04:00
omap_control_usb_device_mode ( ctrl_phy ) ;
2013-01-25 14:23:57 +04:00
break ;
case USB_MODE_DISCONNECT :
2014-03-06 18:38:37 +04:00
omap_control_usb_set_sessionend ( ctrl_phy ) ;
2013-01-25 14:23:57 +04:00
break ;
default :
dev_vdbg ( dev , " invalid omap control usb mode \n " ) ;
}
}
EXPORT_SYMBOL_GPL ( omap_control_usb_set_mode ) ;
2013-10-03 19:12:31 +04:00
# ifdef CONFIG_OF
2014-03-06 18:38:37 +04:00
static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS ;
static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2 ;
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3 ;
static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2 ;
static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2 ;
2013-10-03 19:12:31 +04:00
2014-03-06 18:38:37 +04:00
static const struct of_device_id omap_control_phy_id_table [ ] = {
2013-10-03 19:12:31 +04:00
{
. compatible = " ti,control-phy-otghs " ,
. data = & otghs_data ,
} ,
{
. compatible = " ti,control-phy-usb2 " ,
. data = & usb2_data ,
} ,
{
. compatible = " ti,control-phy-pipe3 " ,
. data = & pipe3_data ,
} ,
{
2014-03-07 09:48:00 +04:00
. compatible = " ti,control-phy-usb2-dra7 " ,
2013-10-03 19:12:31 +04:00
. data = & dra7usb2_data ,
} ,
2013-10-15 14:02:14 +04:00
{
2014-03-07 09:48:00 +04:00
. compatible = " ti,control-phy-usb2-am437 " ,
2013-10-15 14:02:14 +04:00
. data = & am437usb2_data ,
} ,
2013-10-03 19:12:31 +04:00
{ } ,
} ;
2014-03-06 18:38:37 +04:00
MODULE_DEVICE_TABLE ( of , omap_control_phy_id_table ) ;
2013-10-03 19:12:31 +04:00
# endif
2014-03-06 18:38:37 +04:00
static int omap_control_phy_probe ( struct platform_device * pdev )
2013-01-25 14:23:57 +04:00
{
struct resource * res ;
2013-10-03 19:12:31 +04:00
const struct of_device_id * of_id ;
2014-03-06 18:38:37 +04:00
struct omap_control_phy * control_phy ;
2013-10-03 19:12:31 +04:00
2014-03-06 18:38:37 +04:00
of_id = of_match_device ( of_match_ptr ( omap_control_phy_id_table ) ,
& pdev - > dev ) ;
2013-10-03 19:12:31 +04:00
if ( ! of_id )
return - EINVAL ;
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
control_phy = devm_kzalloc ( & pdev - > dev , sizeof ( * control_phy ) ,
2013-01-25 14:23:57 +04:00
GFP_KERNEL ) ;
2014-03-06 18:38:37 +04:00
if ( ! control_phy ) {
dev_err ( & pdev - > dev , " unable to alloc memory for control phy \n " ) ;
2013-01-25 14:23:57 +04:00
return - ENOMEM ;
}
2014-03-06 18:38:37 +04:00
control_phy - > dev = & pdev - > dev ;
control_phy - > type = * ( enum omap_control_phy_type * ) of_id - > data ;
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
if ( control_phy - > type = = OMAP_CTRL_TYPE_OTGHS ) {
2013-01-25 14:23:57 +04:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
" otghs_control " ) ;
2014-03-06 18:38:37 +04:00
control_phy - > otghs_control = devm_ioremap_resource (
2013-01-25 14:23:57 +04:00
& pdev - > dev , res ) ;
2014-03-06 18:38:37 +04:00
if ( IS_ERR ( control_phy - > otghs_control ) )
return PTR_ERR ( control_phy - > otghs_control ) ;
2013-10-03 19:12:31 +04:00
} else {
2013-01-25 14:23:57 +04:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
2013-10-03 19:12:31 +04:00
" power " ) ;
2014-03-06 18:38:37 +04:00
control_phy - > power = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( control_phy - > power ) ) {
2013-10-03 19:12:31 +04:00
dev_err ( & pdev - > dev , " Couldn't get power register \n " ) ;
2014-03-06 18:38:37 +04:00
return PTR_ERR ( control_phy - > power ) ;
2013-10-03 19:12:31 +04:00
}
}
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
if ( control_phy - > type = = OMAP_CTRL_TYPE_PIPE3 ) {
control_phy - > sys_clk = devm_clk_get ( control_phy - > dev ,
2013-01-25 14:23:57 +04:00
" sys_clkin " ) ;
2014-03-06 18:38:37 +04:00
if ( IS_ERR ( control_phy - > sys_clk ) ) {
2013-01-25 14:23:57 +04:00
pr_err ( " %s: unable to get sys_clkin \n " , __func__ ) ;
return - EINVAL ;
}
}
2014-03-06 18:38:37 +04:00
dev_set_drvdata ( control_phy - > dev , control_phy ) ;
2013-01-25 14:23:57 +04:00
return 0 ;
}
2014-03-06 18:38:37 +04:00
static struct platform_driver omap_control_phy_driver = {
. probe = omap_control_phy_probe ,
2013-01-25 14:23:57 +04:00
. driver = {
2014-03-06 18:38:37 +04:00
. name = " omap-control-phy " ,
2013-01-25 14:23:57 +04:00
. owner = THIS_MODULE ,
2014-03-06 18:38:37 +04:00
. of_match_table = of_match_ptr ( omap_control_phy_id_table ) ,
2013-01-25 14:23:57 +04:00
} ,
} ;
2014-03-06 18:38:37 +04:00
static int __init omap_control_phy_init ( void )
2013-01-25 14:23:57 +04:00
{
2014-03-06 18:38:37 +04:00
return platform_driver_register ( & omap_control_phy_driver ) ;
2013-01-25 14:23:57 +04:00
}
2014-03-06 18:38:37 +04:00
subsys_initcall ( omap_control_phy_init ) ;
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
static void __exit omap_control_phy_exit ( void )
2013-01-25 14:23:57 +04:00
{
2014-03-06 18:38:37 +04:00
platform_driver_unregister ( & omap_control_phy_driver ) ;
2013-01-25 14:23:57 +04:00
}
2014-03-06 18:38:37 +04:00
module_exit ( omap_control_phy_exit ) ;
2013-01-25 14:23:57 +04:00
2014-03-06 18:38:37 +04:00
MODULE_ALIAS ( " platform: omap_control_phy " ) ;
2013-01-25 14:23:57 +04:00
MODULE_AUTHOR ( " Texas Instruments Inc. " ) ;
2014-03-06 18:38:37 +04:00
MODULE_DESCRIPTION ( " OMAP Control Module PHY Driver " ) ;
2013-01-25 14:23:57 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;