2012-02-15 17:04:56 +09:00
/**
* dwc3 - exynos . c - Samsung EXYNOS DWC3 Specific Glue layer
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Author : Anton Tikhomirov < av . tikhomirov @ 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/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/platform_data/dwc3-exynos.h>
# include <linux/dma-mapping.h>
# include <linux/clk.h>
2012-07-19 14:01:10 +03:00
# include <linux/usb/otg.h>
# include <linux/usb/nop-usb-xceiv.h>
2012-02-15 17:04:56 +09:00
# include "core.h"
struct dwc3_exynos {
struct platform_device * dwc3 ;
2012-07-19 14:01:10 +03:00
struct platform_device * usb2_phy ;
struct platform_device * usb3_phy ;
2012-02-15 17:04:56 +09:00
struct device * dev ;
struct clk * clk ;
} ;
2012-07-19 14:01:10 +03:00
static int __devinit dwc3_exynos_register_phys ( struct dwc3_exynos * exynos )
{
struct nop_usb_xceiv_platform_data pdata ;
struct platform_device * pdev ;
int ret ;
memset ( & pdata , 0x00 , sizeof ( pdata ) ) ;
pdev = platform_device_alloc ( " nop_usb_xceiv " , 0 ) ;
if ( ! pdev )
return - ENOMEM ;
exynos - > usb2_phy = pdev ;
pdata . type = USB_PHY_TYPE_USB2 ;
ret = platform_device_add_data ( exynos - > usb2_phy , & pdata , sizeof ( pdata ) ) ;
if ( ret )
goto err1 ;
pdev = platform_device_alloc ( " nop_usb_xceiv " , 1 ) ;
if ( ! pdev ) {
ret = - ENOMEM ;
goto err1 ;
}
exynos - > usb3_phy = pdev ;
pdata . type = USB_PHY_TYPE_USB3 ;
ret = platform_device_add_data ( exynos - > usb3_phy , & pdata , sizeof ( pdata ) ) ;
if ( ret )
goto err2 ;
ret = platform_device_add ( exynos - > usb2_phy ) ;
if ( ret )
goto err2 ;
ret = platform_device_add ( exynos - > usb3_phy ) ;
if ( ret )
goto err3 ;
return 0 ;
err3 :
platform_device_del ( exynos - > usb2_phy ) ;
err2 :
platform_device_put ( exynos - > usb3_phy ) ;
err1 :
platform_device_put ( exynos - > usb2_phy ) ;
return ret ;
}
2012-02-15 17:04:56 +09:00
static int __devinit dwc3_exynos_probe ( struct platform_device * pdev )
{
struct dwc3_exynos_data * pdata = pdev - > dev . platform_data ;
struct platform_device * dwc3 ;
struct dwc3_exynos * exynos ;
struct clk * clk ;
int devid ;
int ret = - ENOMEM ;
exynos = kzalloc ( sizeof ( * exynos ) , GFP_KERNEL ) ;
if ( ! exynos ) {
dev_err ( & pdev - > dev , " not enough memory \n " ) ;
goto err0 ;
}
platform_set_drvdata ( pdev , exynos ) ;
devid = dwc3_get_device_id ( ) ;
if ( devid < 0 )
goto err1 ;
2012-07-19 14:01:10 +03:00
ret = dwc3_exynos_register_phys ( exynos ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " couldn't register PHYs \n " ) ;
goto err1 ;
}
2012-02-15 17:04:56 +09:00
dwc3 = platform_device_alloc ( " dwc3 " , devid ) ;
if ( ! dwc3 ) {
dev_err ( & pdev - > dev , " couldn't allocate dwc3 device \n " ) ;
goto err2 ;
}
clk = clk_get ( & pdev - > dev , " usbdrd30 " ) ;
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " couldn't get clock \n " ) ;
ret = - EINVAL ;
goto err3 ;
}
dma_set_coherent_mask ( & dwc3 - > dev , pdev - > dev . coherent_dma_mask ) ;
dwc3 - > dev . parent = & pdev - > dev ;
dwc3 - > dev . dma_mask = pdev - > dev . dma_mask ;
dwc3 - > dev . dma_parms = pdev - > dev . dma_parms ;
exynos - > dwc3 = dwc3 ;
exynos - > dev = & pdev - > dev ;
exynos - > clk = clk ;
clk_enable ( exynos - > clk ) ;
/* PHY initialization */
if ( ! pdata ) {
dev_dbg ( & pdev - > dev , " missing platform data \n " ) ;
} else {
if ( pdata - > phy_init )
pdata - > phy_init ( pdev , pdata - > phy_type ) ;
}
ret = platform_device_add_resources ( dwc3 , pdev - > resource ,
pdev - > num_resources ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " couldn't add resources to dwc3 device \n " ) ;
goto err4 ;
}
ret = platform_device_add ( dwc3 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register dwc3 device \n " ) ;
goto err4 ;
}
return 0 ;
err4 :
if ( pdata & & pdata - > phy_exit )
pdata - > phy_exit ( pdev , pdata - > phy_type ) ;
clk_disable ( clk ) ;
clk_put ( clk ) ;
err3 :
platform_device_put ( dwc3 ) ;
err2 :
dwc3_put_device_id ( devid ) ;
err1 :
kfree ( exynos ) ;
err0 :
return ret ;
}
static int __devexit dwc3_exynos_remove ( struct platform_device * pdev )
{
struct dwc3_exynos * exynos = platform_get_drvdata ( pdev ) ;
struct dwc3_exynos_data * pdata = pdev - > dev . platform_data ;
platform_device_unregister ( exynos - > dwc3 ) ;
2012-07-19 14:01:10 +03:00
platform_device_unregister ( exynos - > usb2_phy ) ;
platform_device_unregister ( exynos - > usb3_phy ) ;
2012-02-15 17:04:56 +09:00
dwc3_put_device_id ( exynos - > dwc3 - > id ) ;
if ( pdata & & pdata - > phy_exit )
pdata - > phy_exit ( pdev , pdata - > phy_type ) ;
clk_disable ( exynos - > clk ) ;
clk_put ( exynos - > clk ) ;
kfree ( exynos ) ;
return 0 ;
}
static struct platform_driver dwc3_exynos_driver = {
. probe = dwc3_exynos_probe ,
. remove = __devexit_p ( dwc3_exynos_remove ) ,
. driver = {
. name = " exynos-dwc3 " ,
} ,
} ;
module_platform_driver ( dwc3_exynos_driver ) ;
MODULE_ALIAS ( " platform:exynos-dwc3 " ) ;
MODULE_AUTHOR ( " Anton Tikhomirov <av.tikhomirov@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " DesignWare USB3 EXYNOS Glue Layer " ) ;