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-11-03 18:00:27 +05:30
# include <linux/of.h>
2012-02-15 17:04:56 +09:00
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-11-19 13:21:48 -05:00
static int dwc3_exynos_register_phys ( struct dwc3_exynos * exynos )
2012-07-19 14:01:10 +03:00
{
struct nop_usb_xceiv_platform_data pdata ;
struct platform_device * pdev ;
int ret ;
memset ( & pdata , 0x00 , sizeof ( pdata ) ) ;
2013-01-25 16:52:01 +05:30
pdev = platform_device_alloc ( " nop_usb_xceiv " , PLATFORM_DEVID_AUTO ) ;
2012-07-19 14:01:10 +03:00
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 ;
2013-01-25 16:52:01 +05:30
pdev = platform_device_alloc ( " nop_usb_xceiv " , PLATFORM_DEVID_AUTO ) ;
2012-07-19 14:01:10 +03:00
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-11-03 18:00:27 +05:30
static u64 dwc3_exynos_dma_mask = DMA_BIT_MASK ( 32 ) ;
2012-11-19 13:21:48 -05:00
static int dwc3_exynos_probe ( struct platform_device * pdev )
2012-02-15 17:04:56 +09:00
{
struct platform_device * dwc3 ;
struct dwc3_exynos * exynos ;
struct clk * clk ;
2012-11-13 11:20:49 +09:00
struct device * dev = & pdev - > dev ;
2012-02-15 17:04:56 +09:00
int ret = - ENOMEM ;
2012-11-13 11:20:49 +09:00
exynos = devm_kzalloc ( dev , sizeof ( * exynos ) , GFP_KERNEL ) ;
2012-02-15 17:04:56 +09:00
if ( ! exynos ) {
2012-11-13 11:20:49 +09:00
dev_err ( dev , " not enough memory \n " ) ;
return - ENOMEM ;
2012-02-15 17:04:56 +09:00
}
2012-11-03 18:00:27 +05:30
/*
* Right now device - tree probed devices don ' t get dma_mask set .
* Since shared usb code relies on it , set it here for now .
* Once we move to full device tree support this will vanish off .
*/
if ( ! pdev - > dev . dma_mask )
pdev - > dev . dma_mask = & dwc3_exynos_dma_mask ;
2012-02-15 17:04:56 +09:00
platform_set_drvdata ( pdev , exynos ) ;
2012-07-19 14:01:10 +03:00
ret = dwc3_exynos_register_phys ( exynos ) ;
if ( ret ) {
2012-11-13 11:20:49 +09:00
dev_err ( dev , " couldn't register PHYs \n " ) ;
return ret ;
2012-07-19 14:01:10 +03:00
}
2012-10-29 18:09:53 +01:00
dwc3 = platform_device_alloc ( " dwc3 " , PLATFORM_DEVID_AUTO ) ;
2012-02-15 17:04:56 +09:00
if ( ! dwc3 ) {
2012-11-13 11:20:49 +09:00
dev_err ( dev , " couldn't allocate dwc3 device \n " ) ;
return - ENOMEM ;
2012-02-15 17:04:56 +09:00
}
2012-11-13 11:20:49 +09:00
clk = devm_clk_get ( dev , " usbdrd30 " ) ;
2012-02-15 17:04:56 +09:00
if ( IS_ERR ( clk ) ) {
2012-11-13 11:20:49 +09:00
dev_err ( dev , " couldn't get clock \n " ) ;
2012-02-15 17:04:56 +09:00
ret = - EINVAL ;
2012-11-13 11:20:49 +09:00
goto err1 ;
2012-02-15 17:04:56 +09:00
}
2012-11-13 11:20:49 +09:00
dma_set_coherent_mask ( & dwc3 - > dev , dev - > coherent_dma_mask ) ;
2012-02-15 17:04:56 +09:00
2012-11-13 11:20:49 +09:00
dwc3 - > dev . parent = dev ;
dwc3 - > dev . dma_mask = dev - > dma_mask ;
dwc3 - > dev . dma_parms = dev - > dma_parms ;
2012-02-15 17:04:56 +09:00
exynos - > dwc3 = dwc3 ;
2012-11-13 11:20:49 +09:00
exynos - > dev = dev ;
2012-02-15 17:04:56 +09:00
exynos - > clk = clk ;
clk_enable ( exynos - > clk ) ;
ret = platform_device_add_resources ( dwc3 , pdev - > resource ,
pdev - > num_resources ) ;
if ( ret ) {
2012-11-13 11:20:49 +09:00
dev_err ( dev , " couldn't add resources to dwc3 device \n " ) ;
goto err2 ;
2012-02-15 17:04:56 +09:00
}
ret = platform_device_add ( dwc3 ) ;
if ( ret ) {
2012-11-13 11:20:49 +09:00
dev_err ( dev , " failed to register dwc3 device \n " ) ;
goto err2 ;
2012-02-15 17:04:56 +09:00
}
return 0 ;
2012-11-13 11:20:49 +09:00
err2 :
2012-02-15 17:04:56 +09:00
clk_disable ( clk ) ;
err1 :
2012-11-13 11:20:49 +09:00
platform_device_put ( dwc3 ) ;
2012-02-15 17:04:56 +09:00
return ret ;
}
2012-11-19 13:26:20 -05:00
static int dwc3_exynos_remove ( struct platform_device * pdev )
2012-02-15 17:04:56 +09:00
{
struct dwc3_exynos * exynos = platform_get_drvdata ( pdev ) ;
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
clk_disable ( exynos - > clk ) ;
return 0 ;
}
2012-11-03 18:00:27 +05:30
# ifdef CONFIG_OF
static const struct of_device_id exynos_dwc3_match [ ] = {
2013-01-24 19:15:30 +05:30
{ . compatible = " samsung,exynos5250-dwusb3 " } ,
2012-11-03 18:00:27 +05:30
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , exynos_dwc3_match ) ;
# endif
2012-10-16 15:15:38 +05:30
# ifdef CONFIG_PM
static int dwc3_exynos_suspend ( struct device * dev )
{
struct dwc3_exynos * exynos = dev_get_drvdata ( dev ) ;
clk_disable ( exynos - > clk ) ;
return 0 ;
}
static int dwc3_exynos_resume ( struct device * dev )
{
struct dwc3_exynos * exynos = dev_get_drvdata ( dev ) ;
clk_enable ( exynos - > clk ) ;
/* runtime set active to reflect active state. */
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
return 0 ;
}
static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( dwc3_exynos_suspend , dwc3_exynos_resume )
} ;
# define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
# else
# define DEV_PM_OPS NULL
# endif /* CONFIG_PM */
2012-02-15 17:04:56 +09:00
static struct platform_driver dwc3_exynos_driver = {
. probe = dwc3_exynos_probe ,
2012-11-19 13:21:08 -05:00
. remove = dwc3_exynos_remove ,
2012-02-15 17:04:56 +09:00
. driver = {
. name = " exynos-dwc3 " ,
2012-11-03 18:00:27 +05:30
. of_match_table = of_match_ptr ( exynos_dwc3_match ) ,
2012-10-16 15:15:38 +05:30
. pm = DEV_PM_OPS ,
2012-02-15 17:04:56 +09:00
} ,
} ;
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 " ) ;