2010-12-09 15:05:01 +03:00
/*
* Copyright ( C ) 2010 ST - Ericsson AB
* Mian Yousaf Kaukab < mian . yousaf . kaukab @ stericsson . com >
*
* Based on omap2430 . c
*
* 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 .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/platform_device.h>
# include "musb_core.h"
struct ux500_glue {
struct device * dev ;
struct platform_device * musb ;
struct clk * clk ;
} ;
# define glue_to_musb(g) platform_get_drvdata(g->musb)
static int ux500_musb_init ( struct musb * musb )
{
musb - > xceiv = otg_get_transceiver ( ) ;
if ( ! musb - > xceiv ) {
pr_err ( " HS USB OTG: no transceiver configured \n " ) ;
return - ENODEV ;
}
return 0 ;
}
static int ux500_musb_exit ( struct musb * musb )
{
otg_put_transceiver ( musb - > xceiv ) ;
return 0 ;
}
static const struct musb_platform_ops ux500_ops = {
. init = ux500_musb_init ,
. exit = ux500_musb_exit ,
} ;
static int __init ux500_probe ( struct platform_device * pdev )
{
struct musb_hdrc_platform_data * pdata = pdev - > dev . platform_data ;
struct platform_device * musb ;
struct ux500_glue * glue ;
struct clk * clk ;
int ret = - ENOMEM ;
glue = kzalloc ( sizeof ( * glue ) , GFP_KERNEL ) ;
if ( ! glue ) {
dev_err ( & pdev - > dev , " failed to allocate glue context \n " ) ;
goto err0 ;
}
musb = platform_device_alloc ( " musb-hdrc " , - 1 ) ;
if ( ! musb ) {
dev_err ( & pdev - > dev , " failed to allocate musb device \n " ) ;
goto err1 ;
}
clk = clk_get ( & pdev - > dev , " usb " ) ;
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
ret = PTR_ERR ( clk ) ;
goto err2 ;
}
ret = clk_enable ( clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to enable clock \n " ) ;
goto err3 ;
}
musb - > dev . parent = & pdev - > dev ;
2011-03-15 18:24:29 +03:00
musb - > dev . dma_mask = pdev - > dev . dma_mask ;
musb - > dev . coherent_dma_mask = pdev - > dev . coherent_dma_mask ;
2010-12-09 15:05:01 +03:00
glue - > dev = & pdev - > dev ;
glue - > musb = musb ;
glue - > clk = clk ;
pdata - > platform_ops = & ux500_ops ;
platform_set_drvdata ( pdev , glue ) ;
ret = platform_device_add_resources ( musb , pdev - > resource ,
pdev - > num_resources ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add resources \n " ) ;
goto err4 ;
}
ret = platform_device_add_data ( musb , pdata , sizeof ( * pdata ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add platform_data \n " ) ;
goto err4 ;
}
ret = platform_device_add ( musb ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register musb device \n " ) ;
goto err4 ;
}
return 0 ;
err4 :
clk_disable ( clk ) ;
err3 :
clk_put ( clk ) ;
err2 :
platform_device_put ( musb ) ;
err1 :
kfree ( glue ) ;
err0 :
return ret ;
}
static int __exit ux500_remove ( struct platform_device * pdev )
{
struct ux500_glue * glue = platform_get_drvdata ( pdev ) ;
platform_device_del ( glue - > musb ) ;
platform_device_put ( glue - > musb ) ;
clk_disable ( glue - > clk ) ;
clk_put ( glue - > clk ) ;
kfree ( glue ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int ux500_suspend ( struct device * dev )
{
struct ux500_glue * glue = dev_get_drvdata ( dev ) ;
struct musb * musb = glue_to_musb ( glue ) ;
otg_set_suspend ( musb - > xceiv , 1 ) ;
clk_disable ( glue - > clk ) ;
return 0 ;
}
static int ux500_resume ( struct device * dev )
{
struct ux500_glue * glue = dev_get_drvdata ( dev ) ;
struct musb * musb = glue_to_musb ( glue ) ;
int ret ;
ret = clk_enable ( glue - > clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable clock \n " ) ;
return ret ;
}
otg_set_suspend ( musb - > xceiv , 0 ) ;
return 0 ;
}
static const struct dev_pm_ops ux500_pm_ops = {
. suspend = ux500_suspend ,
. resume = ux500_resume ,
} ;
# define DEV_PM_OPS (&ux500_pm_ops)
# else
# define DEV_PM_OPS NULL
# endif
static struct platform_driver ux500_driver = {
. remove = __exit_p ( ux500_remove ) ,
. driver = {
. name = " musb-ux500 " ,
. pm = DEV_PM_OPS ,
} ,
} ;
MODULE_DESCRIPTION ( " UX500 MUSB Glue Layer " ) ;
MODULE_AUTHOR ( " Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
static int __init ux500_init ( void )
{
return platform_driver_probe ( & ux500_driver , ux500_probe ) ;
}
subsys_initcall ( ux500_init ) ;
static void __exit ux500_exit ( void )
{
platform_driver_unregister ( & ux500_driver ) ;
}
module_exit ( ux500_exit ) ;